New placements (#119)

* compiles but looks ugly

* create sr record working version

* validate and tsify

* chakra ui v 1.0 icon fixes

* new ui first ver

* remove unused icon

* remove imports , color picker work

* color picker done

* set bg color proper

* suggests vouches page

* remove unused deps

* voting history page

* build analyzer improvements

* calendar page new

* footer stay down

* ui refresh stuff

* x trends dont show tier if no weapons

* draft cup page new look

* plus faq page

* cra 4 beta

* top logo placed center

* weapon images new import style

* top nav aling correctly in mobile resolution

* nav changes

* initial xleaderboard

* peak x powers backend

* graphql codegen

* switched to apollo/client package

* initial xleaderboard table

* insert player ids

* dataloaders

* ts-node dev dep

* x leaderboard basic

* xleaderboard testing

* x rank placements backend

* all leaderboards frontend components initial

* pagination tweaking

* ran prettier

* leaderboards doneish

* top 500 browser basic

* top 500 browser

* pagination tweaks

* implemented filters

* user page placements

* remove useless

* useEffect only call on name change

* markdown use new table style

* use mode images

* search by player id

* link to xsearch from leaderboards

* translated

* disable next if no pagecount

* remove comment
This commit is contained in:
Kalle 2020-10-04 20:48:05 +03:00 committed by GitHub
parent a92c2546b6
commit 710d99cab2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
275 changed files with 10994 additions and 7420 deletions

View File

@ -1,6 +0,0 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": false
}

View File

@ -0,0 +1,9 @@
overwrite: true
schema: "http://localhost:3001/graphql"
documents: "src/graphql/**/*.graphql"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"dependencies": {
"@apollo/react-hooks": "^3.1.5",
"@apollo/client": "^3.3.0-beta.6",
"@chakra-ui/core": "^1.0.0-rc.5",
"@reach/router": "^1.3.4",
"@rehooks/local-storage": "^2.4.0",
@ -18,7 +18,6 @@
"@types/react-select": "^3.0.19",
"@types/recharts": "^1.8.15",
"@types/webpack-env": "^1.15.2",
"apollo-boost": "^0.4.9",
"graphql": "^15.3.0",
"i18next": "^19.7.0",
"i18next-browser-languagedetector": "^6.0.1",
@ -39,7 +38,6 @@
"react-scripts": "next",
"react-select": "^3.1.0",
"react-string-replace": "^0.4.4",
"react-super-responsive-table": "^5.1.2",
"recharts": "^1.8.5",
"source-map-explorer": "^2.5.0",
"typescript": "^3.8.3",
@ -51,7 +49,8 @@
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"analyze": "source-map-explorer 'build/static/js/*.js'"
"analyze": "source-map-explorer 'build/static/js/*.js'",
"gen": "graphql-codegen --config codegen.yml"
},
"eslintConfig": {
"extends": "react-app"
@ -68,5 +67,10 @@
"last 1 safari version"
]
},
"devDependencies": {}
"devDependencies": {
"@graphql-codegen/cli": "^1.17.8",
"@graphql-codegen/typescript": "^1.17.9",
"@graphql-codegen/typescript-operations": "^1.17.8",
"@graphql-codegen/typescript-react-apollo": "^2.0.6"
}
}

View File

@ -1175,7 +1175,6 @@
"ZM": "Zambia",
"ZW": "Zimbabwe"
},
"navigation": {
"Map Planner": "Map Planner",
"Calendar": "Calendar",
@ -1188,6 +1187,7 @@
"Top 500 Browser": "Top 500 Browser",
"Top 500": "Top 500",
"Top 500 Tier Lists": "Top 500 Tier Lists",
"Leaderboards": "Leaderboards",
"Plus Server": "Plus Server",
"Profile": "Profile",
"Log out": "Log out",
@ -1211,7 +1211,9 @@
"Blue": "Blue",
"Cyan": "Cyan",
"Purple": "Purple",
"Pink": "Pink"
"Pink": "Pink",
"paginationNext": "Next",
"paginationPrevious": "Previous"
},
"home": {
"Competitive Splatoon Hub": "Competitive Splatoon Hub",
@ -1535,6 +1537,13 @@
"xtrends": {
"trendsExplanation": "Here you can find X Rank Top 500 usage tier lists. For example for a weapon to be in the X tier it needs at least 30 placements in that mode that month."
},
"xleaderboards": {
"PEAK_XP": "X Power - all weapons",
"UNIQUE_WEAPONS_COUNT": "Top 500 placements with different weapons",
"FOUR_MODE_PEAK_AVERAGE": "4 mode peak X Power average",
"PLACEMENTS_COUNT": "Top 500 placements",
"Score": "Score"
},
"footer": {
"About": "About",
"External links": "External links",

View File

@ -1,28 +1,35 @@
import { Box, Flex, Heading, Image } from "@chakra-ui/core"
import React from "react"
import errorGirl from "./assets/error_girl.png"
import { Box, Flex, Heading, Image } from "@chakra-ui/core";
import React from "react";
import errorGirl from "./assets/error_girl.png";
type HocProps = {
// here you can extend hoc with new props
}
};
type HocState = {
readonly error: Error | null | undefined
}
readonly error: Error | null | undefined;
};
class ErrorBoundary extends React.Component<HocProps, HocState> {
readonly state: HocState = {
error: null,
}
};
componentDidCatch(error: Error | null, errorInfo: object) {
console.log({ error })
this.setState({ error: error })
console.log({ error });
this.setState({ error: error });
}
render() {
if (this.state.error) {
return (
<Flex flexDir="column" justifyContent="center" alignItems="center">
<Flex
flexDir="column"
justifyContent="center"
alignItems="center"
bg="white"
padding="6rem"
borderRadius="5px"
>
<Image w="24rem" mx="auto" src={errorGirl} />
<Heading as="h3" mx="auto">
An <span style={{ color: "red" }}>error</span> occurred:{" "}
@ -62,11 +69,11 @@ class ErrorBoundary extends React.Component<HocProps, HocState> {
<code>{this.state.error.stack}</code>
</pre>
</Flex>
)
);
}
return this.props.children
return this.props.children;
}
}
export default ErrorBoundary
export default ErrorBoundary;

View File

@ -1,46 +1,46 @@
import { createIcon } from "@chakra-ui/icon"
import { createIcon } from "@chakra-ui/icon";
export const DiscordIcon = createIcon({
displayName: "DiscordIcon",
d:
"M40,12c0,0-4.585-3.588-10-4l-0.488,0.976C34.408,10.174,36.654,11.891,39,14c-4.045-2.065-8.039-4-15-4s-10.955,1.935-15,4c2.346-2.109,5.018-4.015,9.488-5.024L18,8c-5.681,0.537-10,4-10,4s-5.121,7.425-6,22c5.162,5.953,13,6,13,6l1.639-2.185C13.857,36.848,10.715,35.121,8,32c3.238,2.45,8.125,5,16,5s12.762-2.55,16-5c-2.715,3.121-5.857,4.848-8.639,5.815L33,40c0,0,7.838-0.047,13-6C45.121,19.425,40,12,40,12z M17.5,30c-1.933,0-3.5-1.791-3.5-4c0-2.209,1.567-4,3.5-4s3.5,1.791,3.5,4C21,28.209,19.433,30,17.5,30z M30.5,30c-1.933,0-3.5-1.791-3.5-4c0-2.209,1.567-4,3.5-4s3.5,1.791,3.5,4C34,28.209,32.433,30,30.5,30z",
viewBox: "0 0 48 48",
})
});
export const TurfWarIcon = createIcon({
displayName: "TurfWarIcon",
d:
"M8 45l9 30 4-4 5-4 3 2c2 3 2 3 1 5l-1 6c1 4 1 4-7 13l-8 8 13 13 13 14 12-12 11-12 12 12 13 11 14-13 13-13-9-8c-8-9-8-9-8-13v-6c-2-2-2-3 1-5l2-3 5 5 5 4 1-4 8-30 7-27a676 676 0 0 0-61 16l4 5 5 5-6 5-5 5-6-5-5-6 4-4 4-5-4-1-30-8-26-7 7 30zm22-18l22 6-3 3-3 4 17 17c11 11 17 17 16 18l-15 8c-3 0-6-2-15-8l-10-5c-2 0-5-2-8-5l-5-5-4 3c-3 2-3 2-3 0A1874 1874 0 0 1 8 21l22 6zm90-4l-6 21-6 20c0 1-2 0-3-2-3-3-3-2-10 3-2 3-6 5-7 5-3 1-4 1-12-7l-9-9 8-7 7-7-3-4-3-3a1474 1474 0 0 1 44-10zM43 79c-2 5 6 12 11 9l4-3c1-1 1-1 1 1 2 4-4 9-10 9s-12-6-12-11c0-4 2-8 5-8 2 0 2 0 1 3zm46-1c2 3 1 9-1 12-1 3-7 5-11 5s-9-4-9-8c0-2 0-3 4 0 3 2 3 2 6 1 5-2 8-5 6-9-1-3-1-3 2-3l3 2zm-36 2c2 2 2 2 1 3-2 2-6 1-7-1-1-4 2-5 6-2zm27 1c0 3-3 4-6 3-1-2-1-2 1-4 3-3 5-2 5 1zM30 99l-5 6-2-2c-2-2-2-2 3-8l6-6 2 2c2 3 2 3-4 8zm36-8l2 5c1 2 3 3 5 3l11 9 7 8-2 2-2 3-13-14C62 95 61 94 61 91c1-2 4-3 5 0zm39 12l-2 2-6-5-5-6 1-2 2-3 6 6 6 5-2 3zm-61-4c2 0 1 1-5 7l-6 7-3-2-2-2 6-6c3-4 6-6 7-5l3 1zm49 4l6 6-2 2-3 2-6-7-6-6 2-2 3-1 6 6zm-34-3l-9 10c-10 10-10 10-12 9l-2-2 8-9 10-8h5z",
viewBox: "0 0 128 128",
})
});
export const SplatZonesIcon = createIcon({
displayName: "SplatZonesIcon",
d:
"M29.2 37.2C1.8 57 1.6 57.2 3.1 58c.7.5 4.1 1.4 7.6 2.1 3.5.7 6.3 1.5 6.3 1.8 0 .3-3.4 3.1-7.5 6.1-4.1 3-7.3 5.9-7.1 6.5.1.5 2 1.3 4.2 1.8 2.1.5 5.6 1.3 7.7 1.8l3.8 1-7.8 6C6 88.4 2.4 91.5 2.2 92c-.2.5 1.2 1.1 3 1.4 1.8.2 13.9 2.3 26.8 4.5s29.6 5.1 37.1 6.3l13.5 2.2 14.6-10.5c8.1-5.7 17.8-12.6 21.7-15.2 3.9-2.6 7.1-5.2 7.1-5.6 0-.5-3.8-1.7-8.5-2.7-4.7-1-8.4-2.2-8.3-2.5.2-.4 4.1-3.5 8.7-6.9 4.6-3.4 7.9-6.5 7.5-6.9-.5-.5-4.2-1.4-8.1-2-4-.7-7.3-1.6-7.3-1.9.1-.4 3.6-3.2 8-6.2s8-5.8 8-6.2c0-.9-3.9-1.6-43.5-8.3-18.1-3-33.8-5.7-34.8-5.9-1-.3-8.2 4.2-18.5 11.6zm51.4.3c15.3 2.5 27.9 4.7 28.1 4.9.5.5-11 8.6-12.2 8.6-.5 0-11.3-1.8-24-4-12.6-2.2-23.9-4-25-4-1.1 0-6.2 2.9-11.3 6.5-7.8 5.5-9.7 6.4-12.2 5.8-1.7-.3-3-.8-3-1.2.1-1 29.2-21.1 30.6-21.1.7 0 13.7 2 29 4.5zm-9.1 16C80.8 55.4 88.7 57 89 57c.2 0-1.9 1.6-4.7 3.5-2.9 1.9-6.3 3.5-7.5 3.5-2.9 0-34.9-5.7-35.5-6.3-.9-.9 10.2-8.6 11.7-8.2.8.2 9.1 2 18.5 4zm34.9 5.4c1.7.7 1.3 1.3-3.3 4.9-5.3 4.2-6.9 4.7-10.5 3.3-1.7-.7-1.1-1.3 4-4.9 6.2-4.3 6.8-4.5 9.8-3.3zm-70.5 6C37.8 66.1 28.6 73 25 73c-4.5 0-4.2-1.2 1.2-5.1 5.4-3.9 7.3-4.5 9.7-3zm33.4 5.5c9.8 1.9 18 3.6 18.3 3.9.2.2-1.6 2-4.1 3.9l-4.4 3.5-10.3-1.9c-10.5-1.9-19.6-3.5-25.6-4.4l-3.4-.5 5.3-4c2.9-2.1 5.5-3.9 5.8-3.9.3 0 8.6 1.5 18.4 3.4zm37.5 7.1c.2.2-2 2.1-4.9 4.3-4.7 3.6-5.5 3.9-8.1 3-3.3-1.2-3.4-1 2.9-5.4 3.5-2.3 5.5-3.1 7.3-2.7 1.4.3 2.6.7 2.8.8zm-59 6.1c5.7.8 11.1 1.8 12 2.3.9.4 7.7 1.6 15 2.6C82 89.6 88 90.7 88 91c0 .3-2.6 2.4-5.7 4.6l-5.8 4-9-1.8c-4.9-.9-14.5-2.6-21.2-3.8-6.7-1.1-15.4-2.7-19.3-3.6l-7.1-1.7 5.5-3.7c4.3-2.8 6.4-3.6 8.9-3.3 1.7.2 7.8 1.1 13.5 1.9z",
viewBox: "0 0 128 128",
})
});
export const TowerControlIcon = createIcon({
displayName: "TowerControlIcon",
d:
"M54 11.1C41.5 22 41 22.7 43.9 24.9c2 1.5 2.2 2.4 1.9 9.6l-.3 7.9-8.5 3.4c-4.7 1.8-14.2 5.4-21.2 7.9C3.7 58 3 58.5 3 61c0 2.3.6 2.9 4 3.9 2.2.6 5.7 2.6 7.6 4.3l3.6 3.2-.3 17.9-.4 17.9 25.2 9.4 25.1 9.3 22.1-10.1 22.1-10.2.1-18c.1-10 .1-18.5 0-18.9-.2-1.1 5.3-5.7 9.8-8.3 2.5-1.5 3.6-2.8 3.6-4.5 0-2.2-1-2.6-19-7-10.4-2.6-20.2-5-21.7-5.3L82 44v-9.4c0-8.5.2-9.4 2-9.9 4.1-1.1 3.6-3.8-1.6-8.5C73.8 8.5 65.9 2 65.2 2c-.4 0-5.5 4.1-11.2 9.1z",
viewBox: "0 0 128 128",
})
});
export const RainmakerIcon = createIcon({
displayName: "RainmakerIcon",
d:
"M62.8 14.9c-6.7 2.2-13.3 10.3-12.6 15.4.3 1.9 1.1 2.3 6.8 2.8 14.9 1.4 22.7 8.7 20 18.7-1.5 5.7-3.2 6.9-6.7 4.4-4.1-2.9-9.3-4.9-14.9-5.7-2.7-.3-6-1.2-7.4-2-2.1-1.1-2.4-1.9-2.1-5.9.2-3.2-.2-4.8-1.1-5.1-1.7-.7-10.2 3-20 8.5-6.5 3.7-7.3 4-10.3 2.9-4.5-1.7-10.5.5-12.3 4.5-1.2 2.3-1.2 3.4.1 7.2.8 2.4 1 4.7.6 5-1.8 1.1-.9 6.5 1.6 8.9C5.8 75.9 7.4 77 8 77c.5 0 1 1.2 1 2.6 0 4.1 2.1 10.2 4.1 12 1.7 1.5 1.9 1.4 3.9-1.7 1.1-1.9 2.3-5.1 2.6-7.2.5-2.9 1.1-3.7 2.8-3.7 3 0 4.2 1 5.9 5.3 2.9 6.9 6.3 5.5 9.7-4 1-2.9 1.9-4 2.7-3.5 1 .6 1 1.7.2 4.7-.6 2.2-.9 6.5-.7 9.6.3 4.4.1 5.6-.9 5.2-1.9-.7-4.1 1.4-4.8 4.7-.9 4-3.5 2.8-3.5-1.5 0-1.8-.7-3.8-1.5-4.5-1.2-1-2.2-.4-6 3.6-6 6.5-6 11-.1 14.2 2.5 1.4 5.5 1.7 15.6 1.5 20.8-.2 34.3-5.1 46.8-16.7 5.3-4.9 6.5-6.8 8-11.7C95 81.8 96 80 97.2 80c.9 0 7.5.7 14.7 1.5 7.3.9 13.6 1.3 14.1 1 1.3-.8-1.4-10.8-4.3-15.8-3-5-7.7-9.5-13.9-13-6.8-3.9-8.6-4.2-11.3-1.7l-2.3 2.2-.7-4.5c-.9-5.8 1.4-10.8 6.8-15.1 3.2-2.6 4.9-3.1 10.8-3.4 6.3-.4 6.9-.6 6.9-2.6 0-3.7-4.4-10-8.5-12.3-5.3-3-14-3.1-19.5-.3-2.1 1.1-4.1 2.4-4.5 3-.4.6-2.6-.1-5.6-1.7-5.4-2.8-12.7-3.8-17.1-2.4z",
viewBox: "0 0 128 128",
})
});
export const ClamBlitzIcon = createIcon({
displayName: "ClamBlitzIcon",
d:
"M49.7 22c-19.5 3.1-32.6 11.3-39.6 24.8-2.3 4.3-3 12.7-1.7 18.5C10.6 74.5 14.7 79 36.1 95c27.2 20.4 33.3 22.5 44.5 15.1 3.2-2.1 7.3-5.3 8.9-7.1 1.7-1.7 6.8-6.4 11.5-10.3 19.7-16.4 22-19.6 22-30.4-.1-11.4-6.5-21.5-19.1-29.8-11.1-7.4-21.3-10.5-36.2-11-6.7-.2-14.8 0-18 .5z",
viewBox: "0 0 128 128",
})
});
export const modeIconMap = {
TW: TurfWarIcon,
@ -48,4 +48,4 @@ export const modeIconMap = {
TC: TowerControlIcon,
RM: RainmakerIcon,
CB: ClamBlitzIcon,
} as const
} as const;

View File

@ -1,70 +1,70 @@
import AD from "./abilityIcons/AD.png"
import BDU from "./abilityIcons/BDU.png"
import BRU from "./abilityIcons/BRU.png"
import CB from "./abilityIcons/CB.png"
import DR from "./abilityIcons/DR.png"
import EMPTY from "./abilityIcons/EMPTY.png"
import H from "./abilityIcons/H.png"
import ISM from "./abilityIcons/ISM.png"
import ISS from "./abilityIcons/ISS.png"
import LDE from "./abilityIcons/LDE.png"
import MPU from "./abilityIcons/MPU.png"
import NS from "./abilityIcons/NS.png"
import OG from "./abilityIcons/OG.png"
import OS from "./abilityIcons/OS.png"
import QR from "./abilityIcons/QR.png"
import QSJ from "./abilityIcons/QSJ.png"
import REC from "./abilityIcons/REC.png"
import RES from "./abilityIcons/RES.png"
import RP from "./abilityIcons/RP.png"
import RSU from "./abilityIcons/RSU.png"
import SCU from "./abilityIcons/SCU.png"
import SJ from "./abilityIcons/SJ.png"
import SPU from "./abilityIcons/SPU.png"
import SS from "./abilityIcons/SS.png"
import SSU from "./abilityIcons/SSU.png"
import T from "./abilityIcons/T.png"
import TI from "./abilityIcons/TI.png"
import UNKNOWN from "./abilityIcons/UNKNOWN.png"
import boingOctoDrawingDark from "./b8ing_dark.png"
import boingOctoDrawingLight from "./b8ing_light.png"
import boingDrawingDark from "./boing_dark.png"
import boingDrawingLight from "./boing_light.png"
import firstPlace from "./first_place.png"
import anchov_games_thumbnail from "./mapThumbnails/ancho-v_games.png"
import arowana_mall_thumbnail from "./mapThumbnails/arowana_mall.png"
import blackbelly_skatepark_thumbnail from "./mapThumbnails/blackbelly_skatepark.png"
import camp_triggerfish_thumbnail from "./mapThumbnails/camp_triggerfish.png"
import goby_arena_thumbnail from "./mapThumbnails/goby_arena.png"
import humpback_pump_track_thumbnail from "./mapThumbnails/humpback_pump_track.png"
import inkblot_art_academy_thumbnail from "./mapThumbnails/inkblot_art_academy.png"
import kelp_dome_thumbnail from "./mapThumbnails/kelp_dome.png"
import makomart_thumbnail from "./mapThumbnails/makomart.png"
import manta_maria_thumbnail from "./mapThumbnails/manta_maria.png"
import moray_towers_thumbnail from "./mapThumbnails/moray_towers.png"
import musselforge_fitness_thumbnail from "./mapThumbnails/musselforge_fitness.png"
import new_albacore_hotel_thumbnail from "./mapThumbnails/new_albacore_hotel.png"
import piranha_pit_thumbnail from "./mapThumbnails/piranha_pit.png"
import port_mackerel_thumbnail from "./mapThumbnails/port_mackerel.png"
import shellendorf_institute_thumbnail from "./mapThumbnails/shellendorf_institute.png"
import skipper_pavilion_thumbnail from "./mapThumbnails/skipper_pavilion.png"
import snapper_canal_thumbnail from "./mapThumbnails/snapper_canal.png"
import starfish_mainstage_thumbnail from "./mapThumbnails/starfish_mainstage.png"
import sturgeon_shipyard_thumbnail from "./mapThumbnails/sturgeon_shipyard.png"
import the_reef_thumbnail from "./mapThumbnails/the_reef.png"
import wahoo_world_thumbnail from "./mapThumbnails/wahoo_world.png"
import walleye_warehouse_thumbnail from "./mapThumbnails/walleye_warehouse.png"
import posterGirlDrawingDark from "./poster_girl_dark.png"
import posterGirlDrawingLight from "./poster_girl_light.png"
import secondPlace from "./second_place.png"
import alfonsino from "./Splatoon1Maps/alfonsino.png"
import bluefin from "./Splatoon1Maps/bluefin.png"
import bridge from "./Splatoon1Maps/bridge.png"
import flounder from "./Splatoon1Maps/flounder.png"
import resort from "./Splatoon1Maps/resort.png"
import rig from "./Splatoon1Maps/rig.png"
import underpass from "./Splatoon1Maps/underpass.png"
import thirdPlace from "./third_place.png"
import AD from "./abilityIcons/AD.png";
import BDU from "./abilityIcons/BDU.png";
import BRU from "./abilityIcons/BRU.png";
import CB from "./abilityIcons/CB.png";
import DR from "./abilityIcons/DR.png";
import EMPTY from "./abilityIcons/EMPTY.png";
import H from "./abilityIcons/H.png";
import ISM from "./abilityIcons/ISM.png";
import ISS from "./abilityIcons/ISS.png";
import LDE from "./abilityIcons/LDE.png";
import MPU from "./abilityIcons/MPU.png";
import NS from "./abilityIcons/NS.png";
import OG from "./abilityIcons/OG.png";
import OS from "./abilityIcons/OS.png";
import QR from "./abilityIcons/QR.png";
import QSJ from "./abilityIcons/QSJ.png";
import REC from "./abilityIcons/REC.png";
import RES from "./abilityIcons/RES.png";
import RP from "./abilityIcons/RP.png";
import RSU from "./abilityIcons/RSU.png";
import SCU from "./abilityIcons/SCU.png";
import SJ from "./abilityIcons/SJ.png";
import SPU from "./abilityIcons/SPU.png";
import SS from "./abilityIcons/SS.png";
import SSU from "./abilityIcons/SSU.png";
import T from "./abilityIcons/T.png";
import TI from "./abilityIcons/TI.png";
import UNKNOWN from "./abilityIcons/UNKNOWN.png";
import boingOctoDrawingDark from "./b8ing_dark.png";
import boingOctoDrawingLight from "./b8ing_light.png";
import boingDrawingDark from "./boing_dark.png";
import boingDrawingLight from "./boing_light.png";
import firstPlace from "./first_place.png";
import anchov_games_thumbnail from "./mapThumbnails/ancho-v_games.png";
import arowana_mall_thumbnail from "./mapThumbnails/arowana_mall.png";
import blackbelly_skatepark_thumbnail from "./mapThumbnails/blackbelly_skatepark.png";
import camp_triggerfish_thumbnail from "./mapThumbnails/camp_triggerfish.png";
import goby_arena_thumbnail from "./mapThumbnails/goby_arena.png";
import humpback_pump_track_thumbnail from "./mapThumbnails/humpback_pump_track.png";
import inkblot_art_academy_thumbnail from "./mapThumbnails/inkblot_art_academy.png";
import kelp_dome_thumbnail from "./mapThumbnails/kelp_dome.png";
import makomart_thumbnail from "./mapThumbnails/makomart.png";
import manta_maria_thumbnail from "./mapThumbnails/manta_maria.png";
import moray_towers_thumbnail from "./mapThumbnails/moray_towers.png";
import musselforge_fitness_thumbnail from "./mapThumbnails/musselforge_fitness.png";
import new_albacore_hotel_thumbnail from "./mapThumbnails/new_albacore_hotel.png";
import piranha_pit_thumbnail from "./mapThumbnails/piranha_pit.png";
import port_mackerel_thumbnail from "./mapThumbnails/port_mackerel.png";
import shellendorf_institute_thumbnail from "./mapThumbnails/shellendorf_institute.png";
import skipper_pavilion_thumbnail from "./mapThumbnails/skipper_pavilion.png";
import snapper_canal_thumbnail from "./mapThumbnails/snapper_canal.png";
import starfish_mainstage_thumbnail from "./mapThumbnails/starfish_mainstage.png";
import sturgeon_shipyard_thumbnail from "./mapThumbnails/sturgeon_shipyard.png";
import the_reef_thumbnail from "./mapThumbnails/the_reef.png";
import wahoo_world_thumbnail from "./mapThumbnails/wahoo_world.png";
import walleye_warehouse_thumbnail from "./mapThumbnails/walleye_warehouse.png";
import posterGirlDrawingDark from "./poster_girl_dark.png";
import posterGirlDrawingLight from "./poster_girl_light.png";
import secondPlace from "./second_place.png";
import alfonsino from "./Splatoon1Maps/alfonsino.png";
import bluefin from "./Splatoon1Maps/bluefin.png";
import bridge from "./Splatoon1Maps/bridge.png";
import flounder from "./Splatoon1Maps/flounder.png";
import resort from "./Splatoon1Maps/resort.png";
import rig from "./Splatoon1Maps/rig.png";
import underpass from "./Splatoon1Maps/underpass.png";
import thirdPlace from "./third_place.png";
export const abilityIcons = {
BDU,
@ -96,24 +96,24 @@ export const abilityIcons = {
UNKNOWN,
"": UNKNOWN,
EMPTY,
} as const
} as const;
export const posterGirl = {
light: posterGirlDrawingLight,
dark: posterGirlDrawingDark,
}
};
export const footerSquid = {
light: boingDrawingLight,
dark: boingDrawingDark,
}
};
export const footerOcto = {
light: boingOctoDrawingLight,
dark: boingOctoDrawingDark,
}
};
export const medalEmoji = [null, firstPlace, secondPlace, thirdPlace] as const
export const medalEmoji = [null, firstPlace, secondPlace, thirdPlace] as const;
export const mapIcons: { [key: string]: string } = {
"Arowana Mall": arowana_mall_thumbnail,
@ -139,7 +139,7 @@ export const mapIcons: { [key: string]: string } = {
"The Reef": the_reef_thumbnail,
"Wahoo World": wahoo_world_thumbnail,
"Walleye Warehouse": walleye_warehouse_thumbnail,
} as const
} as const;
export const Splatoon1Maps = [
{
@ -170,4 +170,4 @@ export const Splatoon1Maps = [
image: underpass,
name: "Urchin Underpass",
},
]
];

View File

@ -1,29 +1,29 @@
import React, { useState } from "react"
import PageHeader from "../common/PageHeader"
import { RouteComponentProps, Redirect } from "@reach/router"
import { useQuery, useMutation } from "@apollo/react-hooks"
import { UserData } from "../../types"
import { USER } from "../../graphql/queries/user"
import Loading from "../common/Loading"
import Error from "../common/Error"
import { useMutation, useQuery } from "@apollo/client";
import { Box, Flex, useToast } from "@chakra-ui/core";
import { Redirect, RouteComponentProps } from "@reach/router";
import React, { useState } from "react";
import {
UPDATE_TWITTER,
UpdateTwitterVars,
} from "../../graphql/mutations/updateTwitter"
import { useToast, Flex, Box } from "@chakra-ui/core"
import Input from "../elements/Input"
import Button from "../elements/Button"
import VotingManager from "./VotingManager"
import SubHeader from "../common/SubHeader"
UPDATE_TWITTER,
} from "../../graphql/mutations/updateTwitter";
import { USER } from "../../graphql/queries/user";
import { UserData } from "../../types";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import SubHeader from "../common/SubHeader";
import Button from "../elements/Button";
import Input from "../elements/Input";
import VotingManager from "./VotingManager";
const AdminPage: React.FC<RouteComponentProps> = () => {
const [updateTwitterForms, setUpdateTwitterForms] = useState<
Partial<UpdateTwitterVars>
>({})
const toast = useToast()
>({});
const toast = useToast();
const { data: userData, error: userError, loading: userLoading } = useQuery<
UserData
>(USER)
>(USER);
const [updateTwitter] = useMutation<
{ updateTwitter: boolean },
@ -36,7 +36,7 @@ const AdminPage: React.FC<RouteComponentProps> = () => {
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -45,15 +45,15 @@ const AdminPage: React.FC<RouteComponentProps> = () => {
position: "top-right",
status: "error",
duration: 10000,
})
});
},
})
});
if (userError) return <Error errorMessage={userError.message} />
if (userLoading) return <Loading />
if (!userData!.user) return <Redirect to="/404" />
if (userError) return <Error errorMessage={userError.message} />;
if (userLoading) return <Loading />;
if (!userData!.user) return <Redirect to="/404" />;
if (userData!.user.discord_id !== "79237403620945920")
return <Redirect to="/404" />
return <Redirect to="/404" />;
return (
<>
@ -82,7 +82,7 @@ const AdminPage: React.FC<RouteComponentProps> = () => {
<VotingManager />
</Box>
</>
)
}
);
};
export default AdminPage
export default AdminPage;

View File

@ -1,36 +1,35 @@
import React, { useState } from "react"
import DatePicker from "../elements/DatePicker"
import Loading from "../common/Loading"
import Error from "../common/Error"
import { useQuery, useMutation } from "@apollo/react-hooks"
import { PLUS_INFO, PlusInfoData } from "../../graphql/queries/plusInfo"
import { useMutation, useQuery } from "@apollo/client";
import { Box, useToast } from "@chakra-ui/core";
import React, { useState } from "react";
import { END_VOTING } from "../../graphql/mutations/endVoting";
import {
START_VOTING,
StartVotingVars,
} from "../../graphql/mutations/startVoting"
import { END_VOTING } from "../../graphql/mutations/endVoting"
import { useToast, Box } from "@chakra-ui/core"
import Button from "../elements/Button"
import SubHeader from "../common/SubHeader"
START_VOTING,
} from "../../graphql/mutations/startVoting";
import { PlusInfoData, PLUS_INFO } from "../../graphql/queries/plusInfo";
import Error from "../common/Error";
import Loading from "../common/Loading";
import SubHeader from "../common/SubHeader";
import Button from "../elements/Button";
import DatePicker from "../elements/DatePicker";
const VotingManager: React.FC = () => {
const [endDate, setEndDate] = useState<Date | null>(new Date())
const [confirmed, setConfirmed] = useState(false)
const toast = useToast()
const { data, error, loading } = useQuery<PlusInfoData>(PLUS_INFO)
const [endDate, setEndDate] = useState<Date | null>(new Date());
const [confirmed, setConfirmed] = useState(false);
const toast = useToast();
const { data, error, loading } = useQuery<PlusInfoData>(PLUS_INFO);
const [startVotingMutation] = useMutation<boolean, StartVotingVars>(
START_VOTING,
{
onCompleted: (data) => {
setConfirmed(false)
setConfirmed(false);
toast({
description: `Voting started`,
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -39,7 +38,7 @@ const VotingManager: React.FC = () => {
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [
{
@ -47,17 +46,17 @@ const VotingManager: React.FC = () => {
},
],
}
)
);
const [endVotingMutation] = useMutation(END_VOTING, {
onCompleted: (data) => {
setConfirmed(false)
setConfirmed(false);
toast({
description: `Voting ended`,
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -66,18 +65,18 @@ const VotingManager: React.FC = () => {
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [
{
query: PLUS_INFO,
},
],
})
});
if (loading) return <Loading />
if (error) return <Error errorMessage={error.message} />
if (!data?.plusInfo) return <>No data plusinfo</>
if (loading) return <Loading />;
if (error) return <Error errorMessage={error.message} />;
if (!data?.plusInfo) return <>No data plusinfo</>;
return (
<>
@ -119,7 +118,7 @@ const VotingManager: React.FC = () => {
</>
)}
</>
)
}
);
};
export default VotingManager
export default VotingManager;

View File

@ -1,88 +1,90 @@
import { Badge, Box, Flex, FormLabel, Switch, Wrap } from "@chakra-ui/core"
import { RouteComponentProps, useLocation } from "@reach/router"
import React, { useContext, useEffect, useState } from "react"
import { Helmet } from "react-helmet-async"
import { Trans, useTranslation } from "react-i18next"
import { FiSettings } from "react-icons/fi"
import useAbilityEffects from "../../hooks/useAbilityEffects"
import MyThemeContext from "../../themeContext"
import { Ability, AnalyzerBuild, Weapon } from "../../types"
import PageHeader from "../common/PageHeader"
import WeaponSelector from "../common/WeaponSelector"
import Button from "../elements/Button"
import BuildStats from "./BuildStats"
import EditableBuilds from "./EditableBuilds"
import { Badge, Box, Flex, FormLabel, Switch, Wrap } from "@chakra-ui/core";
import { RouteComponentProps, useLocation } from "@reach/router";
import React, { useContext, useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import { Trans, useTranslation } from "react-i18next";
import { FiSettings } from "react-icons/fi";
import useAbilityEffects from "../../hooks/useAbilityEffects";
import MyThemeContext from "../../themeContext";
import { Ability, AnalyzerBuild, Weapon } from "../../types";
import PageHeader from "../common/PageHeader";
import WeaponSelector from "../common/WeaponSelector";
import Button from "../elements/Button";
import BuildStats from "./BuildStats";
import EditableBuilds from "./EditableBuilds";
const CURRENT_PATCH = "5.3."
const CURRENT_PATCH = "5.3.";
type AnalyzerBuildNoWeapon = Omit<AnalyzerBuild, "weapon">
type AnalyzerBuildNoWeapon = Omit<AnalyzerBuild, "weapon">;
const defaultBuild: AnalyzerBuildNoWeapon = {
headgear: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
clothing: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
shoes: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
}
};
const BuildAnalyzerPage: React.FC<RouteComponentProps> = () => {
const { themeColor, grayWithShade } = useContext(MyThemeContext)
const { t } = useTranslation()
const location = useLocation()
const [build, setBuild] = useState<AnalyzerBuildNoWeapon>({ ...defaultBuild })
const { themeColor, grayWithShade } = useContext(MyThemeContext);
const { t } = useTranslation();
const location = useLocation();
const [build, setBuild] = useState<AnalyzerBuildNoWeapon>({
...defaultBuild,
});
const [otherBuild, setOtherBuild] = useState<AnalyzerBuildNoWeapon>({
...defaultBuild,
})
const [weapon, setWeapon] = useState<Weapon>("Splattershot Jr.")
const [showOther, setShowOther] = useState(false)
const [showNotActualProgress, setShowNotActualProgress] = useState(false)
const [startChartsAtZero, setStartChartsAtZero] = useState(true)
const [otherFocused, setOtherFocused] = useState(false)
const [hideExtra, setHideExtra] = useState(true)
const [showSettings, setShowSettings] = useState(false)
});
const [weapon, setWeapon] = useState<Weapon>("Splattershot Jr.");
const [showOther, setShowOther] = useState(false);
const [showNotActualProgress, setShowNotActualProgress] = useState(false);
const [startChartsAtZero, setStartChartsAtZero] = useState(true);
const [otherFocused, setOtherFocused] = useState(false);
const [hideExtra, setHideExtra] = useState(true);
const [showSettings, setShowSettings] = useState(false);
const [bonusAp, setBonusAp] = useState<Partial<Record<Ability, boolean>>>({})
const [bonusAp, setBonusAp] = useState<Partial<Record<Ability, boolean>>>({});
const [otherBonusAp, setOtherBonusAp] = useState<
Partial<Record<Ability, boolean>>
>({})
const [lde, setLde] = useState(0)
const [otherLde, setOtherLde] = useState(0)
>({});
const [lde, setLde] = useState(0);
const [otherLde, setOtherLde] = useState(0);
const explanations = useAbilityEffects({ ...build, weapon }, bonusAp, lde)
const explanations = useAbilityEffects({ ...build, weapon }, bonusAp, lde);
const otherExplanations = useAbilityEffects(
{ ...otherBuild, weapon },
otherBonusAp,
otherLde
)
);
function getBuildFromUrl() {
const buildToReturn: AnalyzerBuild = {
...defaultBuild,
weapon: "Splattershot Jr.",
}
const decoded = new URLSearchParams(location.search)
};
const decoded = new URLSearchParams(location.search);
for (const [key, value] of decoded) {
switch (key) {
case "weapon":
buildToReturn.weapon = value as Weapon
break
buildToReturn.weapon = value as Weapon;
break;
case "headgear":
case "clothing":
case "shoes":
const abilityKey = key as "headgear" | "clothing" | "shoes"
buildToReturn[abilityKey] = value.split(",") as any
const abilityKey = key as "headgear" | "clothing" | "shoes";
buildToReturn[abilityKey] = value.split(",") as any;
}
}
return buildToReturn
return buildToReturn;
}
useEffect(() => {
const { weapon, ...buildFromUrl } = getBuildFromUrl()
const { weapon, ...buildFromUrl } = getBuildFromUrl();
setWeapon(weapon)
setBuild(buildFromUrl)
setWeapon(weapon);
setBuild(buildFromUrl);
// eslint-disable-next-line
}, [])
}, []);
return (
<>
@ -115,7 +117,7 @@ const BuildAnalyzerPage: React.FC<RouteComponentProps> = () => {
showOther={showOther}
setShowOther={setShowOther}
changeFocus={() => {
setOtherFocused(!otherFocused)
setOtherFocused(!otherFocused);
}}
otherFocused={otherFocused}
bonusAp={bonusAp}
@ -194,7 +196,7 @@ const BuildAnalyzerPage: React.FC<RouteComponentProps> = () => {
</Box>
</Wrap>
</>
)
}
);
};
export default BuildAnalyzerPage
export default BuildAnalyzerPage;

View File

@ -6,23 +6,23 @@ import {
PopoverContent,
PopoverTrigger,
Progress,
} from "@chakra-ui/core"
import React, { useContext, useState } from "react"
import { FaChartLine, FaQuestion } from "react-icons/fa"
import { Explanation } from "../../hooks/useAbilityEffects"
import MyThemeContext from "../../themeContext"
import { Ability, Build } from "../../types"
import { mainOnlyAbilities } from "../../utils/lists"
import AbilityIcon from "../builds/AbilityIcon"
import StatChart from "./StatChart"
} from "@chakra-ui/core";
import React, { useContext, useState } from "react";
import { FaChartLine, FaQuestion } from "react-icons/fa";
import { Explanation } from "../../hooks/useAbilityEffects";
import MyThemeContext from "../../themeContext";
import { Ability, Build } from "../../types";
import { mainOnlyAbilities } from "../../utils/lists";
import AbilityIcon from "../builds/AbilityIcon";
import StatChart from "./StatChart";
interface BuildStatsProps {
explanations: Explanation[]
otherExplanations?: Explanation[]
build: Partial<Build>
hideExtra?: boolean
showNotActualProgress?: boolean
startChartsAtZero?: boolean
explanations: Explanation[];
otherExplanations?: Explanation[];
build: Partial<Build>;
hideExtra?: boolean;
showNotActualProgress?: boolean;
startChartsAtZero?: boolean;
}
const BuildStats: React.FC<BuildStatsProps> = ({
@ -33,41 +33,41 @@ const BuildStats: React.FC<BuildStatsProps> = ({
showNotActualProgress = false,
startChartsAtZero = true,
}) => {
const [expandedCharts, setExpandedCharts] = useState<Set<string>>(new Set())
const [expandedCharts, setExpandedCharts] = useState<Set<string>>(new Set());
const abilityArrays: Ability[][] = [
build.headgear ?? [],
build.clothing ?? [],
build.shoes ?? [],
]
];
const abilityToPoints: Partial<Record<Ability, number>> = {}
const abilityToPoints: Partial<Record<Ability, number>> = {};
abilityArrays.forEach((arr) =>
arr.forEach((ability, index) => {
if (ability !== "UNKNOWN") {
let abilityPoints = index === 0 ? 10 : 3
let abilityPoints = index === 0 ? 10 : 3;
if (mainOnlyAbilities.indexOf(ability as any) !== -1)
abilityPoints = 999
abilityPoints = 999;
abilityToPoints[ability] = abilityToPoints.hasOwnProperty(ability)
? (abilityToPoints[ability] as any) + abilityPoints
: abilityPoints
: abilityPoints;
}
})
)
);
const BuildStat: React.FC<{
title: string
effect: string
otherEffect?: string
ability: Ability
info?: string
progressBarValue: number
otherProgressBarValue?: number
getEffect?: (ap: number) => number
ap: number
otherAp?: number
chartExpanded: boolean
toggleChart: () => void
title: string;
effect: string;
otherEffect?: string;
ability: Ability;
info?: string;
progressBarValue: number;
otherProgressBarValue?: number;
getEffect?: (ap: number) => number;
ap: number;
otherAp?: number;
chartExpanded: boolean;
toggleChart: () => void;
}> = ({
title,
effect,
@ -84,7 +84,7 @@ const BuildStats: React.FC<BuildStatsProps> = ({
}) => {
const { darkerBgColor, themeColorWithShade, colorMode } = useContext(
MyThemeContext
)
);
return (
<>
@ -171,23 +171,23 @@ const BuildStats: React.FC<BuildStatsProps> = ({
</Box>
)}
</>
)
}
);
};
return (
<>
{explanations.map((_explanation, index) => {
const explanation = explanations[index]
const explanation = explanations[index];
const otherExplanation = otherExplanations
? otherExplanations[index]
: undefined
: undefined;
if (
explanation.effectFromMax === 0 &&
(!otherExplanation || otherExplanation.effectFromMax === 0) &&
hideExtra
) {
return null
return null;
}
return (
@ -213,21 +213,21 @@ const BuildStats: React.FC<BuildStatsProps> = ({
otherAp={otherExplanation?.ap}
chartExpanded={expandedCharts.has(explanation.title)}
toggleChart={() => {
const newSet = new Set(expandedCharts)
const newSet = new Set(expandedCharts);
if (newSet.has(explanation.title)) {
newSet.delete(explanation.title)
newSet.delete(explanation.title);
} else {
newSet.add(explanation.title)
newSet.add(explanation.title);
}
setExpandedCharts(newSet)
setExpandedCharts(newSet);
}}
/>
</Box>
)
);
})}
</>
)
}
);
};
export default BuildStats
export default BuildStats;

View File

@ -1,7 +1,7 @@
import { Box, Flex, IconButton } from "@chakra-ui/core"
import React from "react"
import { useTranslation } from "react-i18next"
import { FiCopy, FiEdit, FiSquare } from "react-icons/fi"
import { Box, Flex, IconButton } from "@chakra-ui/core";
import React from "react";
import { useTranslation } from "react-i18next";
import { FiCopy, FiEdit, FiSquare } from "react-icons/fi";
import {
Ability,
AnalyzerBuild,
@ -9,38 +9,38 @@ import {
HeadOnlyAbility,
ShoesOnlyAbility,
StackableAbility,
} from "../../types"
} from "../../types";
import {
clothingOnlyAbilities,
headOnlyAbilities,
shoesOnlyAbilities,
} from "../../utils/lists"
import ViewSlots from "../builds/ViewSlots"
import Button from "../elements/Button"
import AbilityButtons from "../user/AbilityButtons"
import HeadOnlyToggle from "./HeadOnlyToggle"
import LdeSlider from "./LdeSlider"
} from "../../utils/lists";
import ViewSlots from "../builds/ViewSlots";
import Button from "../elements/Button";
import AbilityButtons from "../user/AbilityButtons";
import HeadOnlyToggle from "./HeadOnlyToggle";
import LdeSlider from "./LdeSlider";
interface EditableBuildsProps {
build: Omit<AnalyzerBuild, "weapon">
otherBuild: Omit<AnalyzerBuild, "weapon">
setBuild: React.Dispatch<React.SetStateAction<Omit<AnalyzerBuild, "weapon">>>
showOther: boolean
setShowOther: React.Dispatch<React.SetStateAction<boolean>>
otherFocused: boolean
changeFocus: () => void
bonusAp: Partial<Record<Ability, boolean>>
build: Omit<AnalyzerBuild, "weapon">;
otherBuild: Omit<AnalyzerBuild, "weapon">;
setBuild: React.Dispatch<React.SetStateAction<Omit<AnalyzerBuild, "weapon">>>;
showOther: boolean;
setShowOther: React.Dispatch<React.SetStateAction<boolean>>;
otherFocused: boolean;
changeFocus: () => void;
bonusAp: Partial<Record<Ability, boolean>>;
setBonusAp: React.Dispatch<
React.SetStateAction<Partial<Record<Ability, boolean>>>
>
otherBonusAp: Partial<Record<Ability, boolean>>
>;
otherBonusAp: Partial<Record<Ability, boolean>>;
setOtherBonusAp: React.Dispatch<
React.SetStateAction<Partial<Record<Ability, boolean>>>
>
lde: number
otherLde: number
setLde: React.Dispatch<React.SetStateAction<number>>
setOtherLde: React.Dispatch<React.SetStateAction<number>>
>;
lde: number;
otherLde: number;
setLde: React.Dispatch<React.SetStateAction<number>>;
setOtherLde: React.Dispatch<React.SetStateAction<number>>;
}
const EditableBuilds: React.FC<EditableBuildsProps> = ({
@ -60,9 +60,10 @@ const EditableBuilds: React.FC<EditableBuildsProps> = ({
setLde,
setOtherLde,
}) => {
const { t } = useTranslation()
const buildToEdit = otherFocused ? otherBuild : build
const handleChange = (value: Object) => setBuild({ ...buildToEdit, ...value })
const { t } = useTranslation();
const buildToEdit = otherFocused ? otherBuild : build;
const handleChange = (value: Object) =>
setBuild({ ...buildToEdit, ...value });
const handleAbilityButtonClick = (ability: Ability) => {
if (headOnlyAbilities.indexOf(ability as any) !== -1) {
@ -74,7 +75,7 @@ const EditableBuilds: React.FC<EditableBuildsProps> = ({
buildToEdit.headgear![2],
buildToEdit.headgear![3],
],
})
});
}
} else if (clothingOnlyAbilities.indexOf(ability as any) !== -1) {
if (buildToEdit.clothing![0] === "UNKNOWN") {
@ -85,7 +86,7 @@ const EditableBuilds: React.FC<EditableBuildsProps> = ({
buildToEdit.clothing![2],
buildToEdit.clothing![3],
],
})
});
}
} else if (shoesOnlyAbilities.indexOf(ability as any) !== -1) {
if (buildToEdit.shoes![0] === "UNKNOWN") {
@ -96,67 +97,67 @@ const EditableBuilds: React.FC<EditableBuildsProps> = ({
buildToEdit.shoes![2],
buildToEdit.shoes![3],
],
})
});
}
} else {
const headI = buildToEdit.headgear!.indexOf("UNKNOWN")
const headI = buildToEdit.headgear!.indexOf("UNKNOWN");
if (headI !== -1) {
const copy = buildToEdit.headgear!.slice()
copy[headI] = ability as HeadOnlyAbility | StackableAbility
const copy = buildToEdit.headgear!.slice();
copy[headI] = ability as HeadOnlyAbility | StackableAbility;
handleChange({
headgear: copy,
})
return
});
return;
}
const clothingI = buildToEdit.clothing!.indexOf("UNKNOWN")
const clothingI = buildToEdit.clothing!.indexOf("UNKNOWN");
if (clothingI !== -1) {
const copy = buildToEdit.clothing!.slice()
copy[clothingI] = ability as ClothingOnlyAbility | StackableAbility
const copy = buildToEdit.clothing!.slice();
copy[clothingI] = ability as ClothingOnlyAbility | StackableAbility;
handleChange({
clothing: copy,
})
return
});
return;
}
const shoesI = buildToEdit.shoes!.indexOf("UNKNOWN")
const shoesI = buildToEdit.shoes!.indexOf("UNKNOWN");
if (shoesI !== -1) {
const copy = buildToEdit.shoes!.slice()
copy[shoesI] = ability as ShoesOnlyAbility | StackableAbility
const copy = buildToEdit.shoes!.slice();
copy[shoesI] = ability as ShoesOnlyAbility | StackableAbility;
handleChange({
shoes: copy,
})
});
}
}
}
};
const handleClickBuildAbility = (
slot: "HEAD" | "CLOTHING" | "SHOES",
index: number
) => {
if (slot === "HEAD") {
const copy = buildToEdit.headgear!.slice()
copy[index] = "UNKNOWN"
const copy = buildToEdit.headgear!.slice();
copy[index] = "UNKNOWN";
handleChange({
headgear: copy,
})
});
} else if (slot === "CLOTHING") {
const copy = buildToEdit.clothing!.slice()
copy[index] = "UNKNOWN"
const copy = buildToEdit.clothing!.slice();
copy[index] = "UNKNOWN";
handleChange({
clothing: copy,
})
});
} else {
const copy = buildToEdit.shoes!.slice()
copy[index] = "UNKNOWN"
const copy = buildToEdit.shoes!.slice();
copy[index] = "UNKNOWN";
handleChange({
shoes: copy,
})
});
}
}
};
const headAbility = build.headgear ? build.headgear[0] : "SSU"
const otherHeadAbility = otherBuild.headgear ? otherBuild.headgear[0] : "SSU"
const headAbility = build.headgear ? build.headgear[0] : "SSU";
const otherHeadAbility = otherBuild.headgear ? otherBuild.headgear[0] : "SSU";
return (
<>
@ -164,9 +165,9 @@ const EditableBuilds: React.FC<EditableBuildsProps> = ({
icon={showOther ? <FiSquare /> : <FiCopy />}
onClick={() => {
if (showOther && otherFocused) {
changeFocus()
changeFocus();
}
setShowOther(!showOther)
setShowOther(!showOther);
}}
mt="1em"
mb="2em"
@ -257,7 +258,7 @@ const EditableBuilds: React.FC<EditableBuildsProps> = ({
/>
</Box>
</>
)
}
);
};
export default EditableBuilds
export default EditableBuilds;

View File

@ -1,13 +1,13 @@
import React, { useContext } from "react"
import { Box, Switch, FormLabel, Flex } from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import AbilityIcon from "../builds/AbilityIcon"
import { useTranslation } from "react-i18next"
import React, { useContext } from "react";
import { Box, Switch, FormLabel, Flex } from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
import AbilityIcon from "../builds/AbilityIcon";
import { useTranslation } from "react-i18next";
interface HeadOnlyToggleProps {
ability: "OG" | "CB"
active: boolean
setActive: () => void
ability: "OG" | "CB";
active: boolean;
setActive: () => void;
}
const HeadOnlyToggle: React.FC<HeadOnlyToggleProps> = ({
@ -15,8 +15,8 @@ const HeadOnlyToggle: React.FC<HeadOnlyToggleProps> = ({
active,
setActive,
}) => {
const { themeColor, themeColorWithShade } = useContext(MyThemeContext)
const { t } = useTranslation()
const { themeColor, themeColorWithShade } = useContext(MyThemeContext);
const { t } = useTranslation();
return (
<Flex
justifyContent="center"
@ -57,7 +57,7 @@ const HeadOnlyToggle: React.FC<HeadOnlyToggleProps> = ({
</Box>
)}
</Flex>
)
}
);
};
export default HeadOnlyToggle
export default HeadOnlyToggle;

View File

@ -7,34 +7,34 @@ import {
NumberInputField,
NumberInputStepper,
Text,
} from "@chakra-ui/core"
import React, { useContext } from "react"
import { Trans, useTranslation } from "react-i18next"
import MyThemeContext from "../../themeContext"
import AbilityIcon from "../builds/AbilityIcon"
} from "@chakra-ui/core";
import React, { useContext } from "react";
import { Trans, useTranslation } from "react-i18next";
import MyThemeContext from "../../themeContext";
import AbilityIcon from "../builds/AbilityIcon";
interface LdeSliderProps {
value: number
setValue: (value: number) => void
value: number;
setValue: (value: number) => void;
}
const LdeSlider: React.FC<LdeSliderProps> = ({ value, setValue }) => {
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext)
const { t } = useTranslation()
const bonusAp = Math.floor((24 / 21) * value)
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext);
const { t } = useTranslation();
const bonusAp = Math.floor((24 / 21) * value);
const getLdeEffect = () => {
if (value === 21) return t("analyzer;ldeFullEffectExplanation")
if (value === 21) return t("analyzer;ldeFullEffectExplanation");
const pointMark = 51 - value
const pointMark = 51 - value;
if (value > 0)
return (
<Trans i18nKey="analyzer;ldeInBetweenExplanation">
Enemy has reached the {{ pointMark }} point mark
</Trans>
)
return t("analyzer;ldeNoEffectExplanation")
}
);
return t("analyzer;ldeNoEffectExplanation");
};
return (
<Flex
justifyContent="center"
@ -88,7 +88,7 @@ const LdeSlider: React.FC<LdeSliderProps> = ({ value, setValue }) => {
{getLdeEffect()}
</Box>
</Flex>
)
}
);
};
export default LdeSlider
export default LdeSlider;

View File

@ -1,4 +1,4 @@
import React, { useContext } from "react"
import React, { useContext } from "react";
import {
CartesianGrid,
Legend,
@ -8,17 +8,17 @@ import {
Tooltip,
XAxis,
YAxis,
} from "recharts"
import MyThemeContext from "../../themeContext"
import { possibleAps } from "../../utils/lists"
import { useTranslation } from "react-i18next"
} from "recharts";
import MyThemeContext from "../../themeContext";
import { possibleAps } from "../../utils/lists";
import { useTranslation } from "react-i18next";
interface StatChartProps {
title: string
getEffect: (ap: number) => number
ap: number
otherAp?: number
startChartsAtZero: boolean
title: string;
getEffect: (ap: number) => number;
ap: number;
otherAp?: number;
startChartsAtZero: boolean;
}
const StatChart: React.FC<StatChartProps> = ({
@ -28,14 +28,14 @@ const StatChart: React.FC<StatChartProps> = ({
getEffect,
startChartsAtZero,
}) => {
const { t } = useTranslation()
const { themeColorHex, darkerBgColor } = useContext(MyThemeContext)
const { t } = useTranslation();
const { themeColorHex, darkerBgColor } = useContext(MyThemeContext);
const getData = () =>
possibleAps.map((ap) => ({ name: `${ap}AP`, [title]: getEffect(ap) }))
possibleAps.map((ap) => ({ name: `${ap}AP`, [title]: getEffect(ap) }));
const CustomizedDot = (props: any) => {
const { cx, cy, payload } = props
const { cx, cy, payload } = props;
if (payload.name === `${ap}${t("analyzer;abilityPointShort")}`) {
return (
@ -56,7 +56,7 @@ const StatChart: React.FC<StatChartProps> = ({
fill="#DD6B20"
/>
</svg>
)
);
}
if (payload.name === `${otherAp}${t("analyzer;abilityPointShort")}`) {
@ -78,11 +78,11 @@ const StatChart: React.FC<StatChartProps> = ({
fill="#3182CE"
/>
</svg>
)
);
}
return null
}
return null;
};
return (
<ResponsiveContainer width="100%" height={300}>
@ -109,7 +109,7 @@ const StatChart: React.FC<StatChartProps> = ({
/>
</LineChart>
</ResponsiveContainer>
)
}
);
};
export default StatChart
export default StatChart;

View File

@ -1,6 +1,6 @@
import React from "react"
import { Ability } from "../../types"
import { abilityIcons } from "../../assets/imageImports"
import React from "react";
import { Ability } from "../../types";
import { abilityIcons } from "../../assets/imageImports";
//https://github.com/loadout-ink/splat2-calc
@ -9,11 +9,11 @@ const sizeMap = {
SUB: "40px",
TINY: "30px",
SUBTINY: "20px",
} as const
} as const;
interface AbilityIconProps {
ability: Ability | "EMPTY"
size: "MAIN" | "SUB" | "TINY" | "SUBTINY"
ability: Ability | "EMPTY";
size: "MAIN" | "SUB" | "TINY" | "SUBTINY";
}
const AbilityIcon: React.FC<AbilityIconProps> = ({ ability, size }) => {
@ -36,7 +36,7 @@ const AbilityIcon: React.FC<AbilityIconProps> = ({ ability, size }) => {
}}
alt={ability}
/>
)
}
);
};
export default AbilityIcon
export default AbilityIcon;

View File

@ -1,24 +1,24 @@
import React, { useState } from "react"
import FieldsetWithLegend from "../common/FieldsetWithLegend"
import { Flex, Box } from "@chakra-ui/core"
import { abilitiesGameOrder } from "../../utils/lists"
import AbilityIcon from "./AbilityIcon"
import { Ability } from "../../types"
import Button from "../elements/Button"
import { FaFilter } from "react-icons/fa"
import { useTranslation } from "react-i18next"
import React, { useState } from "react";
import FieldsetWithLegend from "../common/FieldsetWithLegend";
import { Flex, Box } from "@chakra-ui/core";
import { abilitiesGameOrder } from "../../utils/lists";
import AbilityIcon from "./AbilityIcon";
import { Ability } from "../../types";
import Button from "../elements/Button";
import { FaFilter } from "react-icons/fa";
import { useTranslation } from "react-i18next";
interface AbilitySelectorProps {
abilities: Ability[]
setAbilities: (abilities: Ability[]) => void
abilities: Ability[];
setAbilities: (abilities: Ability[]) => void;
}
const AbilitySelector: React.FC<AbilitySelectorProps> = ({
abilities,
setAbilities,
}) => {
const [show, setShow] = useState(false)
const { t } = useTranslation()
const [show, setShow] = useState(false);
const { t } = useTranslation();
return show ? (
<>
<FieldsetWithLegend
@ -35,8 +35,8 @@ const AbilitySelector: React.FC<AbilitySelectorProps> = ({
p="5px"
cursor={abilities.indexOf(ability) === -1 ? "pointer" : undefined}
onClick={() => {
if (abilities.indexOf(ability) !== -1) return
setAbilities(abilities.concat(ability))
if (abilities.indexOf(ability) !== -1) return;
setAbilities(abilities.concat(ability));
}}
>
<AbilityIcon
@ -83,7 +83,7 @@ const AbilitySelector: React.FC<AbilitySelectorProps> = ({
<Button icon={<FaFilter />} onClick={() => setShow(!show)}>
{t("builds;Filter by ability")}
</Button>
)
}
);
};
export default AbilitySelector
export default AbilitySelector;

View File

@ -8,29 +8,29 @@ import {
PopoverBody,
PopoverContent,
PopoverTrigger,
} from "@chakra-ui/core"
import { Link } from "@reach/router"
import React, { useContext, useEffect, useState } from "react"
import { Trans, useTranslation } from "react-i18next"
import { FiBarChart2, FiEdit, FiInfo, FiTarget } from "react-icons/fi"
import top500 from "../../assets/top500.png"
import MyThemeContext from "../../themeContext"
import { Build } from "../../types"
import Flag from "../common/Flag"
import WeaponImage from "../common/WeaponImage"
import BuildCardStats from "./BuildCardStats"
import Gears from "./Gears"
import ViewAP from "./ViewAP"
import ViewSlots from "./ViewSlots"
} from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext, useEffect, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { FiBarChart2, FiEdit, FiInfo, FiTarget } from "react-icons/fi";
import top500logo from "../../assets/top500.png";
import MyThemeContext from "../../themeContext";
import { Build } from "../../types";
import Flag from "../common/Flag";
import WeaponImage from "../common/WeaponImage";
import BuildCardStats from "./BuildCardStats";
import Gears from "./Gears";
import ViewAP from "./ViewAP";
import ViewSlots from "./ViewSlots";
interface BuildCardProps {
build: Build
defaultToAPView: boolean
showUser?: boolean
canModify?: boolean
setBuildBeingEdited?: (build: Build) => void
otherBuildCount?: number
onShowAllByUser?: () => void
build: Build;
defaultToAPView: boolean;
showUser?: boolean;
canModify?: boolean;
setBuildBeingEdited?: (build: Build) => void;
otherBuildCount?: number;
onShowAllByUser?: () => void;
}
const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
@ -43,21 +43,21 @@ const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
onShowAllByUser,
...props
}) => {
const [apView, setApView] = useState(defaultToAPView)
const [showStats, setShowStats] = useState(false)
const [apView, setApView] = useState(defaultToAPView);
const [showStats, setShowStats] = useState(false);
const {
themeColor,
darkerBgColor,
grayWithShade,
themeColorWithShade,
} = useContext(MyThemeContext)
const { t } = useTranslation()
} = useContext(MyThemeContext);
const { t } = useTranslation();
useEffect(() => {
setApView(defaultToAPView)
}, [defaultToAPView])
setApView(defaultToAPView);
}, [defaultToAPView]);
const username = build.discord_user!.username
const username = build.discord_user!.username;
return (
<>
@ -80,7 +80,7 @@ const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
</Box>
{build.top && (
<Image
src={top500}
src={top500logo}
alt="x rank top 500 logo"
height="40px"
width="auto"
@ -229,7 +229,7 @@ const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
</Box>
</Box>
</>
)
}
);
};
export default BuildCard
export default BuildCard;

View File

@ -1,41 +1,41 @@
import React from "react"
import useAbilityEffects from "../../hooks/useAbilityEffects"
import { Build, AnalyzerBuild } from "../../types"
import BuildStats from "../analyzer/BuildStats"
import Modal from "../elements/Modal"
import ViewSlots from "./ViewSlots"
import Button from "../elements/Button"
import { Link } from "@reach/router"
import { RiTShirtAirLine } from "react-icons/ri"
import React from "react";
import useAbilityEffects from "../../hooks/useAbilityEffects";
import { Build, AnalyzerBuild } from "../../types";
import BuildStats from "../analyzer/BuildStats";
import Modal from "../elements/Modal";
import ViewSlots from "./ViewSlots";
import Button from "../elements/Button";
import { Link } from "@reach/router";
import { RiTShirtAirLine } from "react-icons/ri";
interface BuildCardStatsProps {
build: Build
closeModal: () => void
build: Build;
closeModal: () => void;
}
const unchangingBonus = {}
const unchangingBonus = {};
const BuildCardStats: React.FC<BuildCardStatsProps> = ({
build,
closeModal,
}) => {
const explanations = useAbilityEffects(build, unchangingBonus, 0)
const explanations = useAbilityEffects(build, unchangingBonus, 0);
const defaultBuild: AnalyzerBuild = {
weapon: build.weapon,
headgear: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
clothing: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
shoes: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
}
const zeroExplanations = useAbilityEffects(defaultBuild, unchangingBonus, 0)
};
const zeroExplanations = useAbilityEffects(defaultBuild, unchangingBonus, 0);
const strippedBuild = {
weapon: build.weapon,
headgear: build.headgear,
clothing: build.clothing,
shoes: build.shoes,
}
const queryParam = new URLSearchParams(strippedBuild as any).toString()
};
const queryParam = new URLSearchParams(strippedBuild as any).toString();
return (
<Modal
@ -57,7 +57,7 @@ const BuildCardStats: React.FC<BuildCardStatsProps> = ({
otherExplanations={zeroExplanations}
/>
</Modal>
)
}
);
};
export default BuildCardStats
export default BuildCardStats;

View File

@ -1,38 +1,38 @@
import { useQuery } from "@apollo/react-hooks"
import { Box, Button, Flex, FormLabel, Heading, Switch } from "@chakra-ui/core"
import { RouteComponentProps } from "@reach/router"
import useLocalStorage from "@rehooks/local-storage"
import React, { useContext, useState } from "react"
import { Helmet } from "react-helmet-async"
import { useTranslation } from "react-i18next"
import InfiniteScroll from "react-infinite-scroller"
import { SEARCH_FOR_BUILDS } from "../../graphql/queries/searchForBuilds"
import MyThemeContext from "../../themeContext"
import { useQuery } from "@apollo/client";
import { Box, Button, Flex, FormLabel, Heading, Switch } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import useLocalStorage from "@rehooks/local-storage";
import React, { useContext, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import InfiniteScroll from "react-infinite-scroller";
import { SEARCH_FOR_BUILDS } from "../../graphql/queries/searchForBuilds";
import MyThemeContext from "../../themeContext";
import {
Ability,
Build,
SearchForBuildsData,
SearchForBuildsVars,
Weapon,
} from "../../types"
import Error from "../common/Error"
import Loading from "../common/Loading"
import PageHeader from "../common/PageHeader"
import WeaponSelector from "../common/WeaponSelector"
import Alert from "../elements/Alert"
import AbilitySelector from "./AbilitySelector"
import BuildCard from "./BuildCard"
} from "../../types";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import WeaponSelector from "../common/WeaponSelector";
import Alert from "../elements/Alert";
import AbilitySelector from "./AbilitySelector";
import BuildCard from "./BuildCard";
const BuildsPage: React.FC<RouteComponentProps> = () => {
const { themeColor } = useContext(MyThemeContext)
const { t } = useTranslation()
const [weapon, setWeapon] = useState<Weapon | null>(null)
const [buildsToShow, setBuildsToShow] = useState(10)
const [abilities, setAbilities] = useState<Ability[]>([])
const [expandedUsers, setExpandedUsers] = useState<Set<string>>(new Set())
const { themeColor } = useContext(MyThemeContext);
const { t } = useTranslation();
const [weapon, setWeapon] = useState<Weapon | null>(null);
const [buildsToShow, setBuildsToShow] = useState(10);
const [abilities, setAbilities] = useState<Ability[]>([]);
const [expandedUsers, setExpandedUsers] = useState<Set<string>>(new Set());
const [prefersAPView, setAPPreference] = useLocalStorage<boolean>(
"prefersAPView"
)
);
const { data, error, loading } = useQuery<
SearchForBuildsData,
@ -40,8 +40,8 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
>(SEARCH_FOR_BUILDS, {
variables: { weapon: weapon as any },
skip: !weapon,
})
if (error) return <Error errorMessage={error.message} />
});
if (error) return <Error errorMessage={error.message} />;
const buildsFilterByAbilities: Build[] = !data
? []
@ -51,28 +51,28 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
...build.headgear,
...build.clothing,
...build.shoes,
])
]);
return abilities.every((ability) =>
abilitiesInBuild.has(ability as any)
)
);
})
: data.searchForBuilds
: data.searchForBuilds;
const usersOtherBuilds: { [key: string]: Build[] } = {}
const usersOtherBuilds: { [key: string]: Build[] } = {};
const buildsOnePerUserUnlessExpanded = buildsFilterByAbilities.reduce(
(buildsArray: Build[], build) => {
const discord_id = build.discord_user!.discord_id
const discord_id = build.discord_user!.discord_id;
if (!usersOtherBuilds[discord_id]) {
usersOtherBuilds[discord_id] = []
return [...buildsArray, build]
usersOtherBuilds[discord_id] = [];
return [...buildsArray, build];
}
usersOtherBuilds[discord_id] = [...usersOtherBuilds[discord_id], build]
return buildsArray
usersOtherBuilds[discord_id] = [...usersOtherBuilds[discord_id], build];
return buildsArray;
},
[]
)
);
return (
<>
@ -127,7 +127,7 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
{buildsOnePerUserUnlessExpanded
.filter((_, index) => index < buildsToShow)
.reduce((buildElementsArray: JSX.Element[], build) => {
const allBuildsByUserToShow = []
const allBuildsByUserToShow = [];
allBuildsByUserToShow.push(
<BuildCard
key={build.id}
@ -153,7 +153,7 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
}
m="0.5em"
/>
)
);
if (expandedUsers.has(build.discord_user!.discord_id)) {
allBuildsByUserToShow.push(
@ -170,10 +170,10 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
m="0.5em"
/>
))
)
);
}
return [...buildElementsArray, ...allBuildsByUserToShow]
return [...buildElementsArray, ...allBuildsByUserToShow];
}, [])}
</Flex>
</InfiniteScroll>
@ -198,7 +198,7 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
</Alert>
)}
</>
)
}
);
};
export default BuildsPage
export default BuildsPage;

View File

@ -1,13 +1,13 @@
import React from "react"
import { HeadGear, ClothingGear, ShoesGear } from "../../types"
import english_internal from "../../utils/english_internal.json"
import { Box } from "@chakra-ui/core"
import { useTranslation } from "react-i18next"
import React from "react";
import { HeadGear, ClothingGear, ShoesGear } from "../../types";
import english_internal from "../../utils/english_internal.json";
import { Box } from "@chakra-ui/core";
import { useTranslation } from "react-i18next";
interface GearImageProps {
englishName?: HeadGear | ClothingGear | ShoesGear
renderNullIfNoName?: boolean
mini?: boolean
englishName?: HeadGear | ClothingGear | ShoesGear;
renderNullIfNoName?: boolean;
mini?: boolean;
}
const GearImage: React.FC<GearImageProps> = ({
@ -15,10 +15,10 @@ const GearImage: React.FC<GearImageProps> = ({
renderNullIfNoName,
mini,
}) => {
const { t } = useTranslation()
if (!englishName && renderNullIfNoName) return null
if (!englishName) return <Box />
const wh = "32px"
const { t } = useTranslation();
if (!englishName && renderNullIfNoName) return null;
if (!englishName) return <Box />;
const wh = "32px";
return (
<img
alt={t(`game;${englishName}`)}
@ -28,7 +28,7 @@ const GearImage: React.FC<GearImageProps> = ({
mini ? { width: wh, height: wh, display: "inline-block" } : undefined
}
/>
)
}
);
};
export default GearImage
export default GearImage;

View File

@ -1,15 +1,15 @@
import React from "react"
import { Build } from "../../types"
import GearImage from "./GearImage"
import { Flex, Box } from "@chakra-ui/core"
import React from "react";
import { Build } from "../../types";
import GearImage from "./GearImage";
import { Flex, Box } from "@chakra-ui/core";
interface GearsProps {
build: Build
build: Build;
}
const Gears: React.FC<GearsProps> = ({ build }) => {
if (!build.headgearItem && !build.clothingItem && !build.shoesItem) {
return <Box h="30px" />
return <Box h="30px" />;
}
return (
<Flex justifyContent="center">
@ -23,7 +23,7 @@ const Gears: React.FC<GearsProps> = ({ build }) => {
<GearImage englishName={build.shoesItem} renderNullIfNoName />
</Box>
</Flex>
)
}
);
};
export default Gears
export default Gears;

View File

@ -1,56 +1,56 @@
// This while is bit of a mess from TS PoV - might be worth while to do it better later
import React, { useContext } from "react"
import { Build, Ability } from "../../types"
import { Box, Flex } from "@chakra-ui/core"
import DividingBox from "../common/DividingBox"
import AbilityIcon from "./AbilityIcon"
import MyThemeContext from "../../themeContext"
import { mainOnlyAbilities } from "../../utils/lists"
import { useTranslation } from "react-i18next"
import React, { useContext } from "react";
import { Build, Ability } from "../../types";
import { Box, Flex } from "@chakra-ui/core";
import DividingBox from "../common/DividingBox";
import AbilityIcon from "./AbilityIcon";
import MyThemeContext from "../../themeContext";
import { mainOnlyAbilities } from "../../utils/lists";
import { useTranslation } from "react-i18next";
interface ViewAPProps {
build: Build
build: Build;
}
const ViewAP: React.FC<ViewAPProps> = ({ build }) => {
const { t } = useTranslation()
const { grayWithShade } = useContext(MyThemeContext)
const { t } = useTranslation();
const { grayWithShade } = useContext(MyThemeContext);
const abilityArrays: Ability[][] = [
build.headgear,
build.clothing,
build.shoes,
]
];
const abilityToPoints: Partial<Record<Ability, number>> = {}
const abilityToPoints: Partial<Record<Ability, number>> = {};
abilityArrays.forEach((arr) =>
arr.forEach((ability, index) => {
let abilityPoints = index === 0 ? 10 : 3
if (mainOnlyAbilities.indexOf(ability as any) !== -1) abilityPoints = 999
let abilityPoints = index === 0 ? 10 : 3;
if (mainOnlyAbilities.indexOf(ability as any) !== -1) abilityPoints = 999;
abilityToPoints[ability] = abilityToPoints.hasOwnProperty(ability)
? (abilityToPoints[ability] as any) + abilityPoints
: abilityPoints
: abilityPoints;
})
)
);
const pointsToAbilities: Record<string, Ability[]> = {}
;(Object.keys(abilityToPoints) as Array<
keyof typeof abilityToPoints
>).forEach((ability: Ability) => {
const points = abilityToPoints[ability]
const pointsToAbilities: Record<string, Ability[]> = {};
(Object.keys(abilityToPoints) as Array<keyof typeof abilityToPoints>).forEach(
(ability: Ability) => {
const points = abilityToPoints[ability];
pointsToAbilities.hasOwnProperty(points as any)
? pointsToAbilities[points as any].push(ability)
: (pointsToAbilities[points as any] = [ability])
})
pointsToAbilities.hasOwnProperty(points as any)
? pointsToAbilities[points as any].push(ability)
: (pointsToAbilities[points as any] = [ability]);
}
);
const APArrays = (Object.keys(pointsToAbilities) as Array<
keyof typeof pointsToAbilities
>)
.map((points) => [points, pointsToAbilities[points as any]])
.sort((a1, a2) => parseInt(a2[0] as string) - parseInt(a1[0] as string))
.sort((a1, a2) => parseInt(a2[0] as string) - parseInt(a1[0] as string));
let indexToPrintAPAt = APArrays[0][0] === "999" ? 1 : 0
let indexToPrintAPAt = APArrays[0][0] === "999" ? 1 : 0;
return (
<Box mt="2">
@ -101,10 +101,10 @@ const ViewAP: React.FC<ViewAPProps> = ({ build }) => {
</Box>
))}
</Flex>
)
);
})}
</Box>
)
}
);
};
export default ViewAP
export default ViewAP;

View File

@ -1,11 +1,11 @@
import React from "react"
import { Build, Ability } from "../../types"
import AbilityIcon from "./AbilityIcon"
import { Flex, Box, BoxProps } from "@chakra-ui/core"
import React from "react";
import { Build, Ability } from "../../types";
import AbilityIcon from "./AbilityIcon";
import { Flex, Box, BoxProps } from "@chakra-ui/core";
interface ViewSlotsProps {
build: Partial<Build>
onAbilityClick?: (gear: "HEAD" | "CLOTHING" | "SHOES", index: number) => void
build: Partial<Build>;
onAbilityClick?: (gear: "HEAD" | "CLOTHING" | "SHOES", index: number) => void;
}
const ViewSlots: React.FC<ViewSlotsProps & BoxProps> = ({
@ -81,7 +81,7 @@ const ViewSlots: React.FC<ViewSlotsProps & BoxProps> = ({
))}
</Flex>
</Box>
)
}
);
};
export default ViewSlots
export default ViewSlots;

View File

@ -1,50 +1,54 @@
import { useQuery } from "@apollo/react-hooks"
import { Box } from "@chakra-ui/core"
import { RouteComponentProps, useLocation } from "@reach/router"
import React, { useContext, useEffect, useRef, useState } from "react"
import { Helmet } from "react-helmet-async"
import { Trans, useTranslation } from "react-i18next"
import { useQuery } from "@apollo/client";
import { Box } from "@chakra-ui/core";
import { RouteComponentProps, useLocation } from "@reach/router";
import React, { useContext, useEffect, useRef, useState } from "react";
import { Helmet } from "react-helmet-async";
import { Trans, useTranslation } from "react-i18next";
import {
UpcomingEventsData,
UPCOMING_EVENTS,
} from "../../graphql/queries/upcomingEvents"
import MyThemeContext from "../../themeContext"
import { getWeek } from "../../utils/helperFunctions"
import Error from "../common/Error"
import Loading from "../common/Loading"
import PageHeader from "../common/PageHeader"
import SubHeader from "../common/SubHeader"
import Input from "../elements/Input"
import TournamentInfo from "./TournamentInfo"
} from "../../graphql/queries/upcomingEvents";
import MyThemeContext from "../../themeContext";
import { getWeek } from "../../utils/helperFunctions";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import SubHeader from "../common/SubHeader";
import Input from "../elements/Input";
import TournamentInfo from "./TournamentInfo";
const CalendarPage: React.FC<RouteComponentProps> = () => {
const { grayWithShade } = useContext(MyThemeContext)
const { t } = useTranslation()
const { grayWithShade } = useContext(MyThemeContext);
const { t } = useTranslation();
const [filter, setFilter] = useState("")
const [filter, setFilter] = useState("");
const location = useLocation()
const matchingRef = useRef<HTMLDivElement>(null)
const location = useLocation();
const matchingRef = useRef<HTMLDivElement>(null);
const { data, error, loading } = useQuery<UpcomingEventsData>(UPCOMING_EVENTS)
const { data, error, loading } = useQuery<UpcomingEventsData>(
UPCOMING_EVENTS
);
const searchParamsEventName = new URLSearchParams(location.search).get("name")
const searchParamsEventName = new URLSearchParams(location.search).get(
"name"
);
useEffect(() => {
if (!matchingRef?.current) return
if (!matchingRef?.current) return;
matchingRef.current.scrollIntoView({ behavior: "smooth", block: "start" })
})
matchingRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
});
if (loading) return <Loading />
if (error) return <Error errorMessage={error.message} />
if (loading) return <Loading />;
if (error) return <Error errorMessage={error.message} />;
const events = data!.upcomingEvents
const events = data!.upcomingEvents;
let lastPrintedWeek: number | null = null
const thisWeekNumber = getWeek(new Date())
let lastPrintedWeek: number | null = null;
const thisWeekNumber = getWeek(new Date());
const timeNow = new Date().toTimeString()
const timeNow = new Date().toTimeString();
const filteredTournaments = events.filter(
(event) =>
@ -52,7 +56,7 @@ const CalendarPage: React.FC<RouteComponentProps> = () => {
`${event.poster_discord_user.username}#${event.poster_discord_user.discord_id}`
.toLowerCase()
.includes(filter.toLowerCase())
)
);
return (
<>
@ -67,12 +71,12 @@ const CalendarPage: React.FC<RouteComponentProps> = () => {
m="3rem 0 2rem"
/>
{filteredTournaments.map((event) => {
const time = new Date(parseInt(event.date))
const weekNumber = getWeek(time)
const printWeekHeader = weekNumber !== lastPrintedWeek
const time = new Date(parseInt(event.date));
const weekNumber = getWeek(time);
const printWeekHeader = weekNumber !== lastPrintedWeek;
if (printWeekHeader) {
lastPrintedWeek = weekNumber
lastPrintedWeek = weekNumber;
}
return (
@ -101,7 +105,7 @@ const CalendarPage: React.FC<RouteComponentProps> = () => {
/>
</div>
</React.Fragment>
)
);
})}
<Box color={grayWithShade}>
<Trans i18nKey="calendar;footer">
@ -112,7 +116,7 @@ const CalendarPage: React.FC<RouteComponentProps> = () => {
</Trans>
</Box>
</>
)
}
);
};
export default CalendarPage
export default CalendarPage;

View File

@ -1,24 +1,24 @@
import { useQuery } from "@apollo/react-hooks"
import { Box, Flex, Heading, Image } from "@chakra-ui/core"
import { Link } from "@reach/router"
import React, { useContext, useState } from "react"
import { useTranslation } from "react-i18next"
import { FiClock, FiEdit, FiInfo } from "react-icons/fi"
import { DiscordIcon } from "../../assets/icons"
import { CompetitiveFeedEvent } from "../../graphql/queries/upcomingEvents"
import { USER } from "../../graphql/queries/user"
import MyThemeContext from "../../themeContext"
import { UserData } from "../../types"
import Section from "../common/Section"
import UserAvatar from "../common/UserAvatar"
import IconButton from "../elements/IconButton"
import Markdown from "../elements/Markdown"
import TournamentModal from "./TournamentModal"
import { useQuery } from "@apollo/client";
import { Box, Flex, Heading, Image } from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import { FiClock, FiEdit, FiInfo } from "react-icons/fi";
import { DiscordIcon } from "../../assets/icons";
import { CompetitiveFeedEvent } from "../../graphql/queries/upcomingEvents";
import { USER } from "../../graphql/queries/user";
import MyThemeContext from "../../themeContext";
import { UserData } from "../../types";
import Section from "../common/Section";
import UserAvatar from "../common/UserAvatar";
import IconButton from "../elements/IconButton";
import Markdown from "../elements/Markdown";
import TournamentModal from "./TournamentModal";
interface TournamentInfoProps {
tournament: CompetitiveFeedEvent
date: Date
expandedByDefault?: boolean
tournament: CompetitiveFeedEvent;
date: Date;
expandedByDefault?: boolean;
}
const TournamentInfo: React.FC<TournamentInfoProps> = ({
@ -26,13 +26,13 @@ const TournamentInfo: React.FC<TournamentInfoProps> = ({
date,
expandedByDefault,
}) => {
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext)
const { i18n } = useTranslation()
const [expanded, setExpanded] = useState(!!expandedByDefault)
const [showModal, setShowModal] = useState(false)
const poster = tournament.poster_discord_user
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext);
const { i18n } = useTranslation();
const [expanded, setExpanded] = useState(!!expandedByDefault);
const [showModal, setShowModal] = useState(false);
const poster = tournament.poster_discord_user;
const { data: userData } = useQuery<UserData>(USER)
const { data: userData } = useQuery<UserData>(USER);
return (
<Section my={5}>
@ -127,7 +127,7 @@ const TournamentInfo: React.FC<TournamentInfoProps> = ({
</Box>
)}
</Section>
)
}
);
};
export default TournamentInfo
export default TournamentInfo;

View File

@ -1,4 +1,4 @@
import { useMutation } from "@apollo/react-hooks"
import { useMutation } from "@apollo/client";
import {
Box,
Flex,
@ -7,40 +7,42 @@ import {
FormHelperText,
Image,
useToast,
} from "@chakra-ui/core"
import React, { useState } from "react"
} from "@chakra-ui/core";
import React, { useState } from "react";
import {
DeleteCompetitiveFeedEventVars,
DELETE_COMPETITIVE_FEED_EVENT,
} from "../../graphql/mutations/deleteCompetitiveFeedEvent"
} from "../../graphql/mutations/deleteCompetitiveFeedEvent";
import {
UpdateCompetitiveFeedEventVars,
UPDATE_COMPETITIVE_FEED_EVENT,
} from "../../graphql/mutations/updateCompetitiveFeedEvent"
} from "../../graphql/mutations/updateCompetitiveFeedEvent";
import {
CompetitiveFeedEvent,
UPCOMING_EVENTS,
} from "../../graphql/queries/upcomingEvents"
import Button from "../elements/Button"
import DatePicker from "../elements/DatePicker"
import Input from "../elements/Input"
import Label from "../elements/Label"
import Modal from "../elements/Modal"
import MarkdownInput from "../user/MarkdownInput"
} from "../../graphql/queries/upcomingEvents";
import Button from "../elements/Button";
import DatePicker from "../elements/DatePicker";
import Input from "../elements/Input";
import Label from "../elements/Label";
import Modal from "../elements/Modal";
import MarkdownInput from "../user/MarkdownInput";
interface TournamentModalProps {
closeModal: () => void
competitiveFeedEvent: CompetitiveFeedEvent
closeModal: () => void;
competitiveFeedEvent: CompetitiveFeedEvent;
}
const TournamentModal: React.FC<TournamentModalProps> = ({
closeModal,
competitiveFeedEvent,
}) => {
const [event, setEvent] = useState<CompetitiveFeedEvent>(competitiveFeedEvent)
const [showErrors, setShowErrors] = useState(false)
const [deleting, setDeleting] = useState(false)
const toast = useToast()
const [event, setEvent] = useState<CompetitiveFeedEvent>(
competitiveFeedEvent
);
const [showErrors, setShowErrors] = useState(false);
const [deleting, setDeleting] = useState(false);
const toast = useToast();
const [updateCompetitiveFeedEvent, { loading }] = useMutation<
boolean,
@ -59,13 +61,13 @@ const TournamentModal: React.FC<TournamentModalProps> = ({
} as UpdateCompetitiveFeedEventVars),
},
onCompleted: () => {
closeModal()
closeModal();
toast({
description: `Event updated`,
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -74,10 +76,10 @@ const TournamentModal: React.FC<TournamentModalProps> = ({
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [{ query: UPCOMING_EVENTS }],
})
});
const [deleteCompetitiveFeedEvent, { loading: deleteLoading }] = useMutation<
boolean,
@ -85,13 +87,13 @@ const TournamentModal: React.FC<TournamentModalProps> = ({
>(DELETE_COMPETITIVE_FEED_EVENT, {
variables: { message_discord_id: event.message_discord_id },
onCompleted: (data) => {
closeModal()
closeModal();
toast({
description: `Event deleted`,
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -100,17 +102,17 @@ const TournamentModal: React.FC<TournamentModalProps> = ({
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [{ query: UPCOMING_EVENTS }],
})
});
const handleChange = (newValueObject: Partial<CompetitiveFeedEvent>) => {
setEvent({ ...event, ...newValueObject })
}
setEvent({ ...event, ...newValueObject });
};
const date = new Date(parseInt(event.date))
const dateNow = new Date()
const date = new Date(parseInt(event.date));
const dateNow = new Date();
const handleSubmit = () => {
if (
@ -121,12 +123,12 @@ const TournamentModal: React.FC<TournamentModalProps> = ({
!event.description ||
event.description.length > 3000
) {
setShowErrors(true)
return
setShowErrors(true);
return;
}
updateCompetitiveFeedEvent()
}
updateCompetitiveFeedEvent();
};
return (
<Modal title="Editing upcoming event info" closeModal={closeModal}>
@ -247,7 +249,7 @@ const TournamentModal: React.FC<TournamentModalProps> = ({
</Button>
</Flex>
</Modal>
)
}
);
};
export default TournamentModal
export default TournamentModal;

View File

@ -1,12 +1,12 @@
import React, { useContext } from "react"
import { Box } from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import React, { useContext } from "react";
import { Box } from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
interface DividingBoxProps {
children: React.ReactNode
location: "top" | "left" | "bottom" | "right"
margin?: string
width?: string
children: React.ReactNode;
location: "top" | "left" | "bottom" | "right";
margin?: string;
width?: string;
}
const DividingBox: React.FC<DividingBoxProps> = ({
@ -15,7 +15,7 @@ const DividingBox: React.FC<DividingBoxProps> = ({
width = undefined,
margin = "0.4em",
}) => {
const { borderStyle } = useContext(MyThemeContext)
const { borderStyle } = useContext(MyThemeContext);
return (
<Box
borderLeft={location === "left" ? borderStyle : undefined}
@ -30,7 +30,7 @@ const DividingBox: React.FC<DividingBoxProps> = ({
>
{children}
</Box>
)
}
);
};
export default DividingBox
export default DividingBox;

View File

@ -1,12 +1,12 @@
import React from "react"
import Alert from "../elements/Alert"
import React from "react";
import Alert from "../elements/Alert";
interface ErrorProps {
errorMessage: string
errorMessage: string;
}
const Error: React.FC<ErrorProps> = ({ errorMessage }) => {
return <Alert status="error">{errorMessage}</Alert>
}
return <Alert status="error">{errorMessage}</Alert>;
};
export default Error
export default Error;

View File

@ -1,14 +1,14 @@
import { Box } from "@chakra-ui/core"
import React, { useContext } from "react"
import MyThemeContext from "../../themeContext"
import { Box } from "@chakra-ui/core";
import React, { useContext } from "react";
import MyThemeContext from "../../themeContext";
interface FieldsetWithLegendProps {
children: React.ReactNode
title: React.ReactNode
titleFontSize: string
dividerMode?: boolean
centerTitle?: boolean
fullWidth?: boolean
children: React.ReactNode;
title: React.ReactNode;
titleFontSize: string;
dividerMode?: boolean;
centerTitle?: boolean;
fullWidth?: boolean;
}
const FieldsetWithLegend: React.FC<FieldsetWithLegendProps & any> = ({
@ -20,7 +20,7 @@ const FieldsetWithLegend: React.FC<FieldsetWithLegendProps & any> = ({
fullWidth = false,
...props
}) => {
const { borderStyle, textColor } = useContext(MyThemeContext)
const { borderStyle, textColor } = useContext(MyThemeContext);
return (
<Box
as="fieldset"
@ -46,7 +46,7 @@ const FieldsetWithLegend: React.FC<FieldsetWithLegendProps & any> = ({
</Box>
{children}
</Box>
)
}
);
};
export default FieldsetWithLegend
export default FieldsetWithLegend;

View File

@ -1,14 +1,14 @@
import React from "react"
import { CountryCode } from "../../types"
import { useTranslation } from "react-i18next"
import React from "react";
import { CountryCode } from "../../types";
import { useTranslation } from "react-i18next";
interface FlagProps {
code: CountryCode
size?: "16" | "32"
code: CountryCode;
size?: "16" | "32";
}
const Flag: React.FC<FlagProps> = ({ code, size = "16" }) => {
const { t } = useTranslation()
const { t } = useTranslation();
return (
<img
src={`https://www.countryflags.io/${code}/flat/${size}.png`}
@ -21,7 +21,7 @@ const Flag: React.FC<FlagProps> = ({ code, size = "16" }) => {
alt={t(`countries;${code.toUpperCase()}`)}
title={t(`countries;${code.toUpperCase()}`)}
/>
)
}
);
};
export default Flag
export default Flag;

View File

@ -1,18 +1,18 @@
import React, { useState, useEffect, useContext } from "react"
import { Spinner, Box } from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import React, { useState, useEffect, useContext } from "react";
import { Spinner, Box } from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
const Loading: React.FC = () => {
const { themeColorWithShade } = useContext(MyThemeContext)
const [showSpinner, setShowSpinner] = useState(false)
const { themeColorWithShade } = useContext(MyThemeContext);
const [showSpinner, setShowSpinner] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setShowSpinner(true), 1000)
const timer = setTimeout(() => setShowSpinner(true), 1000);
return () => clearTimeout(timer)
}, [])
return () => clearTimeout(timer);
}, []);
if (!showSpinner) return null
if (!showSpinner) return null;
return (
<Box textAlign="center" pt="2em">
@ -23,7 +23,7 @@ const Loading: React.FC = () => {
speed="0.65s"
/>
</Box>
)
}
);
};
export default Loading
export default Loading;

View File

@ -1,14 +1,14 @@
import React from "react"
import { Heading } from "@chakra-ui/core"
import { useContext } from "react"
import MyThemeContext from "../../themeContext"
import React from "react";
import { Heading } from "@chakra-ui/core";
import { useContext } from "react";
import MyThemeContext from "../../themeContext";
interface PageHeaderProps {
title: string
title: string;
}
const PageHeader: React.FC<PageHeaderProps> = ({ title }) => {
const { themeColorWithShade } = useContext(MyThemeContext)
const { themeColorWithShade } = useContext(MyThemeContext);
return (
<>
<Heading
@ -22,7 +22,7 @@ const PageHeader: React.FC<PageHeaderProps> = ({ title }) => {
{title}
</Heading>
</>
)
}
);
};
export default PageHeader
export default PageHeader;

View File

@ -1,6 +1,5 @@
.pagination {
display: inline-block;
padding: 8px 16px;
text-decoration: none;
font-weight: bold;
font-size: 18px;
@ -12,10 +11,10 @@
margin: 0.2em;
}
li:first-child {
margin-left: 0;
}
.active-page {
border-bottom: 3px solid;
}
.disabled-page {
display: none;
}

View File

@ -1,28 +1,50 @@
import React from "react"
import "./Pagination.css"
import ReactPaginate from "react-paginate"
import React from "react";
import { useTranslation } from "react-i18next";
import ReactPaginate from "react-paginate";
import Button from "../elements/Button";
import "./Pagination.css";
interface PaginationProps {
pageCount: number
currentPage: number
onChange: (page: number) => void
pageCount: number;
currentPage: number;
onChange: (page: number) => void;
scrollToTop?: boolean;
}
const Pagination: React.FC<PaginationProps> = ({
pageCount,
onChange,
currentPage,
scrollToTop = false,
}) => {
const { t } = useTranslation();
console.log({ currentPage, pageCount });
return (
<ReactPaginate
previousLabel={<>&laquo;</>}
nextLabel={<>&raquo;</>}
breakLabel={"..."}
breakClassName={"page"}
previousLabel={
<Button my={2} disabled={currentPage === 1} size="sm">
{t("navigation;paginationPrevious")}
</Button>
}
nextLabel={
<Button
my={2}
disabled={currentPage === pageCount || !pageCount}
size="sm"
>
{t("navigation;paginationNext")}
</Button>
}
breakLabel="..."
breakClassName="page"
pageCount={pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={3}
onPageChange={({ selected }) => onChange(selected + 1)}
onPageChange={({ selected }) => {
onChange(selected + 1);
if (scrollToTop) window.scrollTo(0, 0);
}}
forcePage={currentPage - 1}
containerClassName="pagination"
pageClassName="page"
@ -31,9 +53,9 @@ const Pagination: React.FC<PaginationProps> = ({
pageLinkClassName="page"
activeLinkClassName="active-page"
disabledClassName="disabled-page"
activeClassName={"active"}
activeClassName="active"
/>
)
}
);
};
export default Pagination
export default Pagination;

View File

@ -1,13 +1,13 @@
import { Box, BoxProps } from "@chakra-ui/core"
import React, { useContext } from "react"
import MyThemeContext from "../../themeContext"
import { Box, BoxProps } from "@chakra-ui/core";
import React, { useContext } from "react";
import MyThemeContext from "../../themeContext";
interface SectionProps {
children: React.ReactNode
children: React.ReactNode;
}
const Section: React.FC<SectionProps & BoxProps> = ({ children, ...props }) => {
const { darkerBgColor } = useContext(MyThemeContext)
const { darkerBgColor } = useContext(MyThemeContext);
return (
<Box
bg={darkerBgColor}
@ -18,7 +18,7 @@ const Section: React.FC<SectionProps & BoxProps> = ({ children, ...props }) => {
>
{children}
</Box>
)
}
);
};
export default Section
export default Section;

View File

@ -1,28 +1,28 @@
import React, { useContext } from "react"
import { ReactComponent as ColorDefs } from "../../assets/splatnet/colorDefs.svg"
import React, { useContext } from "react";
import { ReactComponent as ColorDefs } from "../../assets/splatnet/colorDefs.svg";
import { ReactComponent as Kills } from "../../assets/splatnet/kills.svg"
import { ReactComponent as Deaths } from "../../assets/splatnet/deaths.svg"
import { ReactComponent as Kills } from "../../assets/splatnet/kills.svg";
import { ReactComponent as Deaths } from "../../assets/splatnet/deaths.svg";
import { ReactComponent as Baller } from "../../assets/splatnet/baller.svg"
import { ReactComponent as BooyahBomb } from "../../assets/splatnet/booyah-bomb.svg"
import { ReactComponent as BubbleBlower } from "../../assets/splatnet/bubbleblower.svg"
import { ReactComponent as BurstBombRush } from "../../assets/splatnet/burstbomb-rush.svg"
import { ReactComponent as InkArmor } from "../../assets/splatnet/inkarmor.svg"
import { ReactComponent as Inkjet } from "../../assets/splatnet/inkjet.svg"
import { ReactComponent as Inkstorm } from "../../assets/splatnet/inkstorm.svg"
import { ReactComponent as Missiles } from "../../assets/splatnet/missiles.svg"
import { ReactComponent as Stingray } from "../../assets/splatnet/ray.svg"
import { ReactComponent as Splashdown } from "../../assets/splatnet/splashdown.svg"
import { ReactComponent as UltraStamp } from "../../assets/splatnet/stamp.svg"
import { ReactComponent as SuctionRush } from "../../assets/splatnet/suction-rush.svg"
import { ReactComponent as SplatBombRush } from "../../assets/splatnet/splatbomb-rush.svg"
import { ReactComponent as AutoBombRush } from "../../assets/splatnet/autobomb-rush.svg"
import { ReactComponent as CurlingBombRush } from "../../assets/splatnet/curlingbomb-rush.svg"
import { ReactComponent as Baller } from "../../assets/splatnet/baller.svg";
import { ReactComponent as BooyahBomb } from "../../assets/splatnet/booyah-bomb.svg";
import { ReactComponent as BubbleBlower } from "../../assets/splatnet/bubbleblower.svg";
import { ReactComponent as BurstBombRush } from "../../assets/splatnet/burstbomb-rush.svg";
import { ReactComponent as InkArmor } from "../../assets/splatnet/inkarmor.svg";
import { ReactComponent as Inkjet } from "../../assets/splatnet/inkjet.svg";
import { ReactComponent as Inkstorm } from "../../assets/splatnet/inkstorm.svg";
import { ReactComponent as Missiles } from "../../assets/splatnet/missiles.svg";
import { ReactComponent as Stingray } from "../../assets/splatnet/ray.svg";
import { ReactComponent as Splashdown } from "../../assets/splatnet/splashdown.svg";
import { ReactComponent as UltraStamp } from "../../assets/splatnet/stamp.svg";
import { ReactComponent as SuctionRush } from "../../assets/splatnet/suction-rush.svg";
import { ReactComponent as SplatBombRush } from "../../assets/splatnet/splatbomb-rush.svg";
import { ReactComponent as AutoBombRush } from "../../assets/splatnet/autobomb-rush.svg";
import { ReactComponent as CurlingBombRush } from "../../assets/splatnet/curlingbomb-rush.svg";
import "./SplatoonIcon.css"
import MyThemeContext from "../../themeContext"
import { Weapon } from "../../types"
import "./SplatoonIcon.css";
import MyThemeContext from "../../themeContext";
import { Weapon } from "../../types";
const weaponToSvg = {
"Sploosh-o-matic": Splashdown,
@ -166,14 +166,14 @@ const weaponToSvg = {
"Kensa Undercover Brella": InkArmor,
kills: Kills,
deaths: Deaths,
} as const
} as const;
interface SplatnetIconProps {
iconFor: "kills" | "deaths" | Weapon
iconFor: "kills" | "deaths" | Weapon;
}
const SplatnetIcon: React.FC<SplatnetIconProps> = ({ iconFor }) => {
const { themeColorHex } = useContext(MyThemeContext)
const { themeColorHex } = useContext(MyThemeContext);
const style = {
width: "35px",
height: "auto",
@ -183,14 +183,14 @@ const SplatnetIcon: React.FC<SplatnetIconProps> = ({ iconFor }) => {
backgroundSize: "125%",
userSelect: "none",
"--main-bg-color": themeColorHex,
} as React.CSSProperties
const Component = weaponToSvg[iconFor]
} as React.CSSProperties;
const Component = weaponToSvg[iconFor];
return (
<>
<Component style={style} />
<ColorDefs style={{ width: 0, height: 0 }} />
</>
)
}
);
};
export default SplatnetIcon
export default SplatnetIcon;

View File

@ -1,24 +1,24 @@
import React, { useContext } from "react"
import "./SubHeader.css"
import MyThemeContext from "../../themeContext"
import React, { useContext } from "react";
import "./SubHeader.css";
import MyThemeContext from "../../themeContext";
interface SubHeaderProps {
children: React.ReactNode
children: React.ReactNode;
}
const SubHeader: React.FC<SubHeaderProps> = ({ children }) => {
const { themeColorHex, themeColorHexLighter, colorMode } = useContext(
MyThemeContext
)
);
const style = {
"--sub-header-border":
colorMode === "dark" ? themeColorHexLighter : themeColorHex,
} as React.CSSProperties
} as React.CSSProperties;
return (
<h2 className="decorated">
<span style={style}>{children}</span>
</h2>
)
}
);
};
export default SubHeader
export default SubHeader;

View File

@ -0,0 +1,91 @@
// https://github.com/chakra-ui/chakra-ui/issues/135#issuecomment-644878591
import { Box, BoxProps } from "@chakra-ui/core";
import React, { useContext } from "react";
import MyThemeContext from "../../themeContext";
/**
* Represents tabular data - that is, information presented in a
* two-dimensional table comprised of rows and columns of cells containing
* data. It renders a `<table>` HTML element.
*/
export function Table(props: BoxProps) {
return (
<Box overflow="auto">
<Box as="table" width="full" {...props} />
</Box>
);
}
/**
* Defines a set of rows defining the head of the columns of the table. It
* renders a `<thead>` HTML element.
*/
export function TableHead(props: BoxProps) {
return <Box as="thead" {...props} />;
}
/**
* Defines a row of cells in a table. The row's cells can then be established
* using a mix of `TableCell` and `TableHeader` elements. It renders a `<tr>`
* HTML element.
*/
export function TableRow(props: BoxProps) {
const { darkerBgColor } = useContext(MyThemeContext);
return (
<Box
as="tr"
{...props}
_even={{ backgroundColor: darkerBgColor }}
borderRadius="5px"
/>
);
}
export function TableHeader(props: BoxProps) {
const { themeColorWithShade } = useContext(MyThemeContext);
return (
<>
<Box
as="th"
px="4"
py="3"
backgroundColor={themeColorWithShade}
textAlign="left"
fontSize="xs"
textColor="black"
textTransform="uppercase"
letterSpacing="wider"
lineHeight="1rem"
fontWeight="medium"
{...props}
/>
</>
);
}
/**
* Encapsulates a set of table rows, indicating that they comprise the body of
* the table. It renders a `<tbody>` HTML element.
*/
export function TableBody(props: BoxProps) {
return <Box as="tbody" {...props} />;
}
/**
* Defines a cell of a table that contains data. It renders a `<td>` HTML
* element.
*/
export function TableCell(props: BoxProps) {
return (
<Box
as="td"
px="4"
py="4"
lineHeight="1.25rem"
whiteSpace="nowrap"
{...props}
/>
);
}

View File

@ -1,10 +1,10 @@
import { Avatar, AvatarProps } from "@chakra-ui/core"
import React, { useState } from "react"
import { Avatar, AvatarProps } from "@chakra-ui/core";
import React, { useState } from "react";
interface UserAvatarProps {
name: string
src?: string
size?: undefined | "2xl" | "xl" | "lg" | "xs" | "sm"
name: string;
src?: string;
size?: undefined | "2xl" | "xl" | "lg" | "xs" | "sm";
}
const UserAvatar: React.FC<UserAvatarProps & AvatarProps> = ({
@ -13,7 +13,7 @@ const UserAvatar: React.FC<UserAvatarProps & AvatarProps> = ({
size,
...props
}) => {
const [isBeingHovered, setIsBeingHovered] = useState(false)
const [isBeingHovered, setIsBeingHovered] = useState(false);
if (src?.includes("a_")) {
return (
<Avatar
@ -24,9 +24,9 @@ const UserAvatar: React.FC<UserAvatarProps & AvatarProps> = ({
onMouseEnter={() => setIsBeingHovered(true)}
onMouseLeave={() => setIsBeingHovered(false)}
/>
)
);
}
return <Avatar name={name} src={`${src}jpg`} size={size} {...props} />
}
return <Avatar name={name} src={`${src}jpg`} size={size} {...props} />;
};
export default UserAvatar
export default UserAvatar;

View File

@ -1,18 +1,18 @@
import React from "react"
import { useQuery } from "@apollo/react-hooks"
import { USERS, UsersData } from "../../graphql/queries/users"
import Error from "./Error"
import Select from "../elements/Select"
import { useQuery } from "@apollo/client";
import React from "react";
import { USERS, UsersData } from "../../graphql/queries/users";
import Select from "../elements/Select";
import Error from "./Error";
interface UserSelectorProps {
id?: string
setValue?: (value: string) => void
id?: string;
setValue?: (value: string) => void;
}
const UserSelector: React.FC<UserSelectorProps> = ({ setValue }) => {
const { data, error, loading } = useQuery<UsersData>(USERS)
const { data, error, loading } = useQuery<UsersData>(USERS);
if (error) return <Error errorMessage={error.message} />
if (error) return <Error errorMessage={error.message} />;
return (
<>
<Select
@ -32,7 +32,7 @@ const UserSelector: React.FC<UserSelectorProps> = ({ setValue }) => {
isDisabled={loading}
/>
</>
)
}
);
};
export default UserSelector
export default UserSelector;

View File

@ -1,13 +1,13 @@
import React from "react"
import { useTranslation } from "react-i18next"
import { Weapon } from "../../types.js"
import english_internal from "../../utils/english_internal.json"
import React from "react";
import { useTranslation } from "react-i18next";
import { Weapon } from "../../types.js";
import english_internal from "../../utils/english_internal.json";
interface WeaponImageProps {
englishName: Weapon
size: "SMALL" | "SMEDIUM" | "MEDIUM" | "BIG"
asInlineBlock?: boolean
noTitle?: boolean
englishName: Weapon;
size: "SMALL" | "SMEDIUM" | "MEDIUM" | "BIG";
asInlineBlock?: boolean;
noTitle?: boolean;
}
const sizeWhMap: Record<
@ -18,7 +18,7 @@ const sizeWhMap: Record<
SMEDIUM: "48px",
MEDIUM: "64px",
BIG: undefined,
}
};
const WeaponImage: React.FC<WeaponImageProps> = ({
englishName,
@ -26,8 +26,8 @@ const WeaponImage: React.FC<WeaponImageProps> = ({
asInlineBlock,
noTitle,
}) => {
const { t } = useTranslation()
const wh = sizeWhMap[size]
const { t } = useTranslation();
const wh = sizeWhMap[size];
return (
<>
@ -42,7 +42,7 @@ const WeaponImage: React.FC<WeaponImageProps> = ({
}}
/>
</>
)
}
);
};
export default WeaponImage
export default WeaponImage;

View File

@ -1,25 +1,25 @@
import { Box, Flex } from "@chakra-ui/core"
import React from "react"
import { useTranslation } from "react-i18next"
import { components } from "react-select"
import { Weapon } from "../../types"
import { Box, Flex } from "@chakra-ui/core";
import React from "react";
import { useTranslation } from "react-i18next";
import { components } from "react-select";
import { Weapon } from "../../types";
import {
weaponSelectOptions,
weaponSelectOptionsWithAlts,
} from "../../utils/lists"
import Select from "../elements/Select"
import WeaponImage from "./WeaponImage"
} from "../../utils/lists";
import Select from "../elements/Select";
import WeaponImage from "./WeaponImage";
interface WeaponSelectorProps {
value?: Weapon | Weapon[] | "" | null
setValue: (value: any) => void
label: string
required?: boolean
autoFocus?: boolean
clearable?: boolean
isMulti?: boolean
menuIsOpen?: boolean
showAlts?: boolean
value?: Weapon | Weapon[] | "" | null;
setValue: (value: any) => void;
label: string;
required?: boolean;
autoFocus?: boolean;
clearable?: boolean;
isMulti?: boolean;
menuIsOpen?: boolean;
showAlts?: boolean;
}
const WeaponSelector: React.FC<WeaponSelectorProps> = ({
@ -33,7 +33,7 @@ const WeaponSelector: React.FC<WeaponSelectorProps> = ({
menuIsOpen,
showAlts,
}) => {
const { t } = useTranslation()
const { t } = useTranslation();
const singleOption = (props: any) => (
<components.Option {...props}>
<Flex alignItems="center" color={props.isFocused ? "black" : undefined}>
@ -43,7 +43,7 @@ const WeaponSelector: React.FC<WeaponSelectorProps> = ({
{props.label}
</Flex>
</components.Option>
)
);
return (
<Select
@ -78,7 +78,7 @@ const WeaponSelector: React.FC<WeaponSelectorProps> = ({
}}
autoFocus={autoFocus}
/>
)
}
);
};
export default WeaponSelector
export default WeaponSelector;

View File

@ -1,11 +1,11 @@
import React from "react"
import { Alert as ChakraAlert, AlertIcon, CloseButton } from "@chakra-ui/core"
import React from "react";
import { Alert as ChakraAlert, AlertIcon, CloseButton } from "@chakra-ui/core";
interface AlertProps {
children: string | string[] | JSX.Element | JSX.Element[]
status: "error" | "success" | "warning" | "info"
mt?: string
onClose?: () => void
children: string | string[] | JSX.Element | JSX.Element[];
status: "error" | "success" | "warning" | "info";
mt?: string;
onClose?: () => void;
}
const Alert: React.FC<AlertProps> = ({
@ -27,7 +27,7 @@ const Alert: React.FC<AlertProps> = ({
/>
)}
</ChakraAlert>
)
}
);
};
export default Alert
export default Alert;

View File

@ -1,21 +1,21 @@
import React from "react"
import React from "react";
import {
Button as ChakraButton,
ButtonProps as ChakraButtonProps,
} from "@chakra-ui/core"
import { useContext } from "react"
import MyThemeContext from "../../themeContext"
} from "@chakra-ui/core";
import { useContext } from "react";
import MyThemeContext from "../../themeContext";
interface ButtonProps {
children: string | string[]
onClick?: () => void
size?: "xs" | "sm" | "lg" | "md"
icon?: ChakraButtonProps["leftIcon"]
width?: string
color?: string
outlined?: boolean
disabled?: boolean
loading?: boolean
children: string | string[];
onClick?: () => void;
size?: "xs" | "sm" | "lg" | "md";
icon?: ChakraButtonProps["leftIcon"];
width?: string;
color?: string;
outlined?: boolean;
disabled?: boolean;
loading?: boolean;
}
const Button: React.FC<ButtonProps & ChakraButtonProps> = ({
@ -30,7 +30,7 @@ const Button: React.FC<ButtonProps & ChakraButtonProps> = ({
outlined = false,
...props
}) => {
const { themeColor } = useContext(MyThemeContext)
const { themeColor } = useContext(MyThemeContext);
return (
<ChakraButton
variant={outlined ? "outline" : "solid"}
@ -45,7 +45,7 @@ const Button: React.FC<ButtonProps & ChakraButtonProps> = ({
>
{children}
</ChakraButton>
)
}
);
};
export default Button
export default Button;

View File

@ -1,16 +1,16 @@
import React, { forwardRef } from "react"
import HackerOneDatePicker from "react-datepicker"
import Button from "./Button"
import "react-datepicker/src/stylesheets/datepicker.scss"
import React, { forwardRef } from "react";
import HackerOneDatePicker from "react-datepicker";
import Button from "./Button";
import "react-datepicker/src/stylesheets/datepicker.scss";
interface DatePickerProps {
date: Date
setDate: (date: Date | null) => void
date: Date;
setDate: (date: Date | null) => void;
}
interface CustomInputProps {
value?: string
onClick?: () => void
value?: string;
onClick?: () => void;
}
const DatePicker: React.FC<DatePickerProps> = ({ date, setDate }) => {
@ -18,7 +18,7 @@ const DatePicker: React.FC<DatePickerProps> = ({ date, setDate }) => {
({ onClick, value }, ref) => (
<Button onClick={onClick as () => void}>{value as string}</Button>
)
)
);
return (
<HackerOneDatePicker
@ -31,7 +31,7 @@ const DatePicker: React.FC<DatePickerProps> = ({ date, setDate }) => {
dateFormat="MMMM d, yyyy h:mm aa"
customInput={<CustomInput />}
/>
)
}
);
};
export default DatePicker
export default DatePicker;

View File

@ -1,14 +1,14 @@
import React from "react"
import WeaponImage from "../common/WeaponImage"
import { weaponCodes, abilityCodes, gearCodes } from "../../utils/lists"
import AbilityIcon from "../builds/AbilityIcon"
import GearImage from "../builds/GearImage"
import sz from "../../assets/sz.png"
import tc from "../../assets/tc.png"
import rm from "../../assets/rm.png"
import cb from "../../assets/cb.png"
import tw from "../../assets/tw.png"
import { Image } from "@chakra-ui/core"
import React from "react";
import WeaponImage from "../common/WeaponImage";
import { weaponCodes, abilityCodes, gearCodes } from "../../utils/lists";
import AbilityIcon from "../builds/AbilityIcon";
import GearImage from "../builds/GearImage";
import sz from "../../assets/sz.png";
import tc from "../../assets/tc.png";
import rm from "../../assets/rm.png";
import cb from "../../assets/cb.png";
import tw from "../../assets/tw.png";
import { Image } from "@chakra-ui/core";
const modeCodes: Record<string, string> = {
turf_war: tw,
@ -16,32 +16,32 @@ const modeCodes: Record<string, string> = {
tower_control: tc,
rainmaker: rm,
clam_blitz: cb,
} as const
} as const;
interface EmojiProps {
value: string
value: string;
}
const Emoji: React.FC<EmojiProps> = (props) => {
const value = props.value.replace(/:/g, "").toLowerCase()
const value = props.value.replace(/:/g, "").toLowerCase();
const weaponName = weaponCodes[value]
const weaponName = weaponCodes[value];
if (!!weaponName)
return (
<WeaponImage englishName={weaponName as any} size="SMALL" asInlineBlock />
)
);
const abilityName = abilityCodes[value]
if (!!abilityName) return <AbilityIcon size="TINY" ability={abilityName} />
const abilityName = abilityCodes[value];
if (!!abilityName) return <AbilityIcon size="TINY" ability={abilityName} />;
const gearName = gearCodes[value]
if (!!gearName) return <GearImage englishName={gearName} mini />
const gearName = gearCodes[value];
if (!!gearName) return <GearImage englishName={gearName} mini />;
const mode = modeCodes[value]
const mode = modeCodes[value];
if (!!mode)
return <Image src={mode} display="inline-block" w="32px" h="32px" />
return <Image src={mode} display="inline-block" w="32px" h="32px" />;
return <>{props.value}</>
}
return <>{props.value}</>;
};
export default Emoji
export default Emoji;

View File

@ -1,19 +1,19 @@
import React from "react"
import React from "react";
import {
IconButton as ChakraIconButton,
IconButtonProps as ChakraIconButtonProps,
} from "@chakra-ui/core"
import { useContext } from "react"
import MyThemeContext from "../../themeContext"
} from "@chakra-ui/core";
import { useContext } from "react";
import MyThemeContext from "../../themeContext";
interface IconButtonProps {
icon: ChakraIconButtonProps["icon"]
onClick?: () => void
colored?: boolean
disabled?: boolean
loading?: boolean
color?: string
size?: "sm" | "md" | "lg"
icon: ChakraIconButtonProps["icon"];
onClick?: () => void;
colored?: boolean;
disabled?: boolean;
loading?: boolean;
color?: string;
size?: "sm" | "md" | "lg";
}
const IconButton: React.FC<IconButtonProps> = ({
@ -25,13 +25,13 @@ const IconButton: React.FC<IconButtonProps> = ({
color,
size = "lg",
}) => {
const { themeColorWithShade } = useContext(MyThemeContext)
const { themeColorWithShade } = useContext(MyThemeContext);
const getColor = () => {
if (color) return color
if (colored) return themeColorWithShade
return undefined
}
if (color) return color;
if (colored) return themeColorWithShade;
return undefined;
};
return (
<ChakraIconButton
@ -45,7 +45,7 @@ const IconButton: React.FC<IconButtonProps> = ({
isDisabled={disabled}
isLoading={loading}
/>
)
}
);
};
export default IconButton
export default IconButton;

View File

@ -5,22 +5,22 @@ import {
InputGroup,
InputLeftAddon,
InputLeftElement,
} from "@chakra-ui/core"
import React, { useContext } from "react"
import { IconType } from "react-icons/lib/cjs"
import MyThemeContext from "../../themeContext"
import Label from "./Label"
} from "@chakra-ui/core";
import React, { useContext } from "react";
import { IconType } from "react-icons/lib/cjs";
import MyThemeContext from "../../themeContext";
import Label from "./Label";
interface InputProps {
value?: string
setValue: (value: string) => void
label: string
limit?: number
required?: boolean
disabled?: boolean
textLeft?: string
size?: "sm" | "md" | "lg"
icon?: IconType
value?: string;
setValue: (value: string) => void;
label: string;
limit?: number;
required?: boolean;
disabled?: boolean;
textLeft?: string;
size?: "sm" | "md" | "lg";
icon?: IconType;
}
const Input: React.FC<InputProps & BoxProps> = ({
@ -40,10 +40,10 @@ const Input: React.FC<InputProps & BoxProps> = ({
grayWithShade,
darkerBgColor,
textColor,
} = useContext(MyThemeContext)
} = useContext(MyThemeContext);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) =>
setValue(event.target.value)
setValue(event.target.value);
return (
<Box {...props}>
@ -77,7 +77,7 @@ const Input: React.FC<InputProps & BoxProps> = ({
</Box>
)}
</Box>
)
}
);
};
export default Input
export default Input;

View File

@ -1,8 +1,8 @@
import React from "react"
import { Box } from "@chakra-ui/core"
import React from "react";
import { Box } from "@chakra-ui/core";
interface LabelProps {
required?: boolean
required?: boolean;
}
const Label: React.FC<LabelProps> = ({ children, required }) => {
@ -15,7 +15,7 @@ const Label: React.FC<LabelProps> = ({ children, required }) => {
</Box>
)}
</Box>
)
}
);
};
export default Label
export default Label;

View File

@ -1,102 +1,110 @@
import React, { useContext } from "react"
import {
Text,
Checkbox,
Code,
Divider,
Link,
List,
Checkbox,
ListItem,
Heading,
Image,
} from "@chakra-ui/core"
import ReactMarkdown from "react-markdown"
import MyThemeContext from "../../themeContext"
import reactStringReplace from "react-string-replace"
import Emoji from "./Emoji"
Link,
List,
ListItem,
Text,
} from "@chakra-ui/core";
import React, { useContext } from "react";
import ReactMarkdown from "react-markdown";
import reactStringReplace from "react-string-replace";
import MyThemeContext from "../../themeContext";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../common/Table";
import Emoji from "./Emoji";
interface MarkdownProps {
value: string
value: string;
}
const Markdown: React.FC<MarkdownProps> = ({ value }) => {
const { themeColorWithShade } = useContext(MyThemeContext)
const { themeColorWithShade } = useContext(MyThemeContext);
//https://github.com/mustaphaturhan/chakra-ui-markdown-renderer/blob/master/src/index.js
const ChakraUIRenderer = () => {
function getCoreProps(props: any) {
return props["data-sourcepos"]
? { "data-sourcepos": props["data-sourcepos"] }
: {}
: {};
}
return {
paragraph: (props: any) => {
const { children } = props
return <Text mb={2}>{children}</Text>
const { children } = props;
return <Text mb={2}>{children}</Text>;
},
emphasis: (props: any) => {
const { children } = props
return <Text as="em">{children}</Text>
const { children } = props;
return <Text as="em">{children}</Text>;
},
blockquote: (props: any) => {
const { children } = props
return <Code p={2}>{children}</Code>
const { children } = props;
return <Code p={2}>{children}</Code>;
},
code: (props: any) => {
const { language, value } = props
const className = language && `language-${language}`
const { language, value } = props;
const className = language && `language-${language}`;
return (
<pre {...getCoreProps(props)}>
<Code p={2} className={className || undefined}>
{value}
</Code>
</pre>
)
);
},
delete: (props: any) => {
const { children } = props
return <Text as="del">{children}</Text>
const { children } = props;
return <Text as="del">{children}</Text>;
},
thematicBreak: Divider,
link: (props: any) => {
const { children } = props
const { children } = props;
return (
<Link color={themeColorWithShade} {...props}>
{children}
</Link>
)
);
},
img: Image,
linkReference: (props: any) => {
const { children } = props
const { children } = props;
return (
<Link color={themeColorWithShade} {...props}>
{children}
</Link>
)
);
},
imageReference: Image,
text: (props: any) => {
const { children } = props
const { children } = props;
return (
<Text as="span" fontFamily="'Rubik', sans-serif">
{reactStringReplace(children, /(:\S+:)/g, (match, i) => (
<Emoji key={i} value={match} />
))}
</Text>
)
);
},
list: (props: any) => {
const { start, ordered, children, depth } = props
const attrs = getCoreProps(props)
const { start, ordered, children, depth } = props;
const attrs = getCoreProps(props);
if (start !== null && start !== 1 && start !== undefined) {
// @ts-ignore
attrs.start = start.toString()
attrs.start = start.toString();
}
let styleType = "disc"
if (ordered) styleType = "decimal"
if (depth === 1) styleType = "circle"
let styleType = "disc";
if (ordered) styleType = "decimal";
if (depth === 1) styleType = "circle";
return (
<List
spacing={24}
@ -107,17 +115,17 @@ const Markdown: React.FC<MarkdownProps> = ({ value }) => {
>
{children}
</List>
)
);
},
listItem: (props: any) => {
const { children, checked } = props
let checkbox = null
const { children, checked } = props;
let checkbox = null;
if (checked !== null && checked !== undefined) {
checkbox = (
<Checkbox isChecked={checked} isReadOnly>
{children}
</Checkbox>
)
);
}
return (
<ListItem
@ -126,11 +134,11 @@ const Markdown: React.FC<MarkdownProps> = ({ value }) => {
>
{checkbox || children}
</ListItem>
)
);
},
definition: () => null,
heading: (props: any) => {
const { children } = props
const { children } = props;
return (
<Heading
my={4}
@ -140,21 +148,44 @@ const Markdown: React.FC<MarkdownProps> = ({ value }) => {
>
{children}
</Heading>
)
);
},
inlineCode: (props: any) => {
const { children } = props
return <Code {...getCoreProps(props)}>{children}</Code>
const { children } = props;
return <Code {...getCoreProps(props)}>{children}</Code>;
},
}
}
table: (props: any) => {
const { children } = props;
return <Table {...getCoreProps(props)}>{children}</Table>;
},
tableHead: (props: any) => {
const { children } = props;
return <TableHead {...getCoreProps(props)}>{children}</TableHead>;
},
tableBody: (props: any) => {
const { children } = props;
return <TableBody {...getCoreProps(props)}>{children}</TableBody>;
},
tableRow: (props: any) => {
const { children } = props;
return <TableRow {...getCoreProps(props)}>{children}</TableRow>;
},
tableCell: (props: any) => {
const { children, isHeader } = props;
if (isHeader) {
return <TableHeader {...getCoreProps(props)}>{children}</TableHeader>;
}
return <TableCell {...getCoreProps(props)}>{children}</TableCell>;
},
};
};
return (
<ReactMarkdown
source={value}
renderers={ChakraUIRenderer()}
disallowedTypes={["imageReference", "image"]}
/>
)
}
);
};
export default Markdown
export default Markdown;

View File

@ -1,15 +1,15 @@
import { Box, Flex } from "@chakra-ui/core"
import React, { useContext, useEffect } from "react"
import { MdClose } from "react-icons/md"
import useOnClickOutside from "use-onclickoutside"
import useBreakPoints from "../../hooks/useBreakPoints"
import MyThemeContext from "../../themeContext"
import IconButton from "./IconButton"
import { Box, Flex } from "@chakra-ui/core";
import React, { useContext, useEffect } from "react";
import { MdClose } from "react-icons/md";
import useOnClickOutside from "use-onclickoutside";
import useBreakPoints from "../../hooks/useBreakPoints";
import MyThemeContext from "../../themeContext";
import IconButton from "./IconButton";
interface ModalProps {
title: string
closeModal?: () => void
closeOnOutsideClick?: boolean
title: string;
closeModal?: () => void;
closeOnOutsideClick?: boolean;
}
const Modal: React.FC<ModalProps> = ({
@ -18,18 +18,18 @@ const Modal: React.FC<ModalProps> = ({
closeModal,
closeOnOutsideClick,
}) => {
const { bgColor } = useContext(MyThemeContext)
const isSmall = useBreakPoints(768)
const ref: any = React.useRef()
useOnClickOutside(ref, closeOnOutsideClick ? (closeModal as any) : null)
const { bgColor } = useContext(MyThemeContext);
const isSmall = useBreakPoints(768);
const ref: any = React.useRef();
useOnClickOutside(ref, closeOnOutsideClick ? (closeModal as any) : null);
useEffect(() => {
document.body.style.overflow = "hidden"
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "visible"
}
}, [])
document.body.style.overflow = "visible";
};
}, []);
return (
<Box
@ -69,7 +69,7 @@ const Modal: React.FC<ModalProps> = ({
</>
</Box>
</Box>
)
}
);
};
export default Modal
export default Modal;

View File

@ -1,17 +1,17 @@
import React, { useContext } from "react"
import React, { useContext } from "react";
import {
RadioGroup as ChakraRadioGroup,
Radio,
Box,
Stack,
} from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
} from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
interface RadioGroupProps {
options: { label: string; value: string }[]
value: string
label?: string
setValue: (value: any) => void
options: { label: string; value: string }[];
value: string;
label?: string;
setValue: (value: any) => void;
}
const RadioGroup: React.FC<RadioGroupProps> = ({
@ -20,7 +20,7 @@ const RadioGroup: React.FC<RadioGroupProps> = ({
options,
label,
}) => {
const { themeColor } = useContext(MyThemeContext)
const { themeColor } = useContext(MyThemeContext);
return (
<>
{label && (
@ -43,7 +43,7 @@ const RadioGroup: React.FC<RadioGroupProps> = ({
</Stack>
</ChakraRadioGroup>
</>
)
}
);
};
export default RadioGroup
export default RadioGroup;

View File

@ -1,48 +1,48 @@
import { Box } from "@chakra-ui/core"
import React, { useContext, useState } from "react"
import { useTranslation } from "react-i18next"
import ReactSelect, { GroupedOptionsType, OptionsType } from "react-select"
import { SelectComponents } from "react-select/src/components"
import MyThemeContext from "../../themeContext"
import { weapons } from "../../utils/lists"
import Label from "./Label"
import { Box } from "@chakra-ui/core";
import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import ReactSelect, { GroupedOptionsType, OptionsType } from "react-select";
import { SelectComponents } from "react-select/src/components";
import MyThemeContext from "../../themeContext";
import { weapons } from "../../utils/lists";
import Label from "./Label";
interface SelectProps {
options?:
| OptionsType<{
label: string
value: string
label: string;
value: string;
}>
| GroupedOptionsType<{
label: string
value: string
}>
width?: string
label?: string
required?: boolean
label: string;
value: string;
}>;
width?: string;
label?: string;
required?: boolean;
value?:
| {
label: string
value: string
label: string;
value: string;
}
| string
| string[]
| null
setValue?: (value: any) => void
autoFocus?: boolean
| null;
setValue?: (value: any) => void;
autoFocus?: boolean;
components?: Partial<
SelectComponents<{
label: string
value: string
label: string;
value: string;
}>
>
clearable?: boolean
isMulti?: boolean
isLoading?: boolean
isDisabled?: boolean
isSearchable?: boolean
menuIsOpen?: boolean
hideMenuBeforeTyping?: boolean
>;
clearable?: boolean;
isMulti?: boolean;
isLoading?: boolean;
isDisabled?: boolean;
isSearchable?: boolean;
menuIsOpen?: boolean;
hideMenuBeforeTyping?: boolean;
}
const Select: React.FC<SelectProps> = ({
@ -66,53 +66,53 @@ const Select: React.FC<SelectProps> = ({
darkerBgColor,
themeColorHex,
themeColorHexLighter,
} = useContext(MyThemeContext)
const { t } = useTranslation()
const [inputValue, setInputValue] = useState("")
} = useContext(MyThemeContext);
const { t } = useTranslation();
const [inputValue, setInputValue] = useState("");
const handleChange = (selectedOption: any) => {
if (!setValue) return
if (!setValue) return;
if (!selectedOption) {
setValue(null)
return
setValue(null);
return;
}
if (Array.isArray(selectedOption)) {
setValue(selectedOption.map((obj) => obj.value))
setValue(selectedOption.map((obj) => obj.value));
} else {
setValue(selectedOption?.value)
setValue(selectedOption?.value);
}
}
};
const getValue = () => {
if (typeof value === "string") {
return {
label: weapons.includes(value as any) ? t(`game;${value}`) : value,
value: value,
}
};
} else if (Array.isArray(value)) {
return value.map((weapon) => ({
label: weapons.includes(weapon as any) ? t(`game;${weapon}`) : weapon,
value: weapon,
}))
}));
}
return value
}
return value;
};
const getOptionColor = (focused: boolean) => {
if (focused) return "black"
if (focused) return "black";
return colorMode === "light" ? "black" : "white"
}
return colorMode === "light" ? "black" : "white";
};
const menuIsOpenCheck = () => {
if (menuIsOpen) return true
if (menuIsOpen) return true;
if (hideMenuBeforeTyping) {
return !!(inputValue.length >= 3)
return !!(inputValue.length >= 3);
}
return undefined
}
return undefined;
};
return (
<Box>
@ -179,7 +179,7 @@ const Select: React.FC<SelectProps> = ({
...styles,
backgroundColor: isFocused ? themeColorHexLighter : undefined,
color: getOptionColor(isFocused),
}
};
},
menu: (styles) => ({ ...styles, zIndex: 999 }),
control: (base) => ({
@ -190,7 +190,7 @@ const Select: React.FC<SelectProps> = ({
}}
/>
</Box>
)
}
);
};
export default Select
export default Select;

View File

@ -1,16 +1,16 @@
import React, { useContext } from "react"
import { Textarea, Box } from "@chakra-ui/core"
import Label from "./Label"
import MyThemeContext from "../../themeContext"
import React, { useContext } from "react";
import { Textarea, Box } from "@chakra-ui/core";
import Label from "./Label";
import MyThemeContext from "../../themeContext";
interface TextAreaProps {
value?: string
setValue: (value: string) => void
label?: string
limit?: number
required?: boolean
height?: string
id?: string
value?: string;
setValue: (value: string) => void;
label?: string;
limit?: number;
required?: boolean;
height?: string;
id?: string;
}
const TextArea: React.FC<TextAreaProps> = ({
@ -23,7 +23,7 @@ const TextArea: React.FC<TextAreaProps> = ({
}) => {
const { themeColorHex, grayWithShade, darkerBgColor } = useContext(
MyThemeContext
)
);
return (
<>
@ -47,7 +47,7 @@ const TextArea: React.FC<TextAreaProps> = ({
</Box>
)}
</>
)
}
);
};
export default TextArea
export default TextArea;

View File

@ -1,11 +1,11 @@
import React, { useState } from "react"
import { RouteComponentProps } from "@reach/router"
import InTheZoneBanner from "./InTheZoneBanner"
import { Box, Flex, Heading } from "@chakra-ui/core"
import Button from "../elements/Button"
import React, { useState } from "react";
import { RouteComponentProps } from "@reach/router";
import InTheZoneBanner from "./InTheZoneBanner";
import { Box, Flex, Heading } from "@chakra-ui/core";
import Button from "../elements/Button";
const EventsPage: React.FC<RouteComponentProps> = () => {
const [runningNumber, setRunningNumber] = useState(10)
const [runningNumber, setRunningNumber] = useState(10);
return (
<>
<Box>
@ -32,7 +32,7 @@ const EventsPage: React.FC<RouteComponentProps> = () => {
</Flex>
</Box>
</>
)
}
);
};
export default EventsPage
export default EventsPage;

View File

@ -1,31 +1,31 @@
import { Box, Flex } from "@chakra-ui/core"
import React from "react"
import { Box, Flex } from "@chakra-ui/core";
import React from "react";
//https://stackoverflow.com/a/19303725
function seededRandom(seed: number) {
var x = Math.sin(seed++) * 10000
return x - Math.floor(x)
var x = Math.sin(seed++) * 10000;
return x - Math.floor(x);
}
//https://stackoverflow.com/a/23603772
function getRandomColor(runningNumber: number, l: number) {
const color = "hsl(" + seededRandom(runningNumber) * 360 + `, 100%, ${l}%)`
return color
const color = "hsl(" + seededRandom(runningNumber) * 360 + `, 100%, ${l}%)`;
return color;
}
//https://codepen.io/chrisgresh/pen/aNjovb
function getRandomGradient(runningNumber: number) {
const newColor1 = getRandomColor(runningNumber, 20)
const newColor2 = getRandomColor(runningNumber, 80)
const angle = Math.round(seededRandom(runningNumber) * 360)
const newColor1 = getRandomColor(runningNumber, 20);
const newColor2 = getRandomColor(runningNumber, 80);
const angle = Math.round(seededRandom(runningNumber) * 360);
return (
"linear-gradient(" + angle + "deg, " + newColor1 + ", " + newColor2 + ")"
)
);
}
interface InTheZoneBannerProps {
runningNumber: number
runningNumber: number;
}
const InTheZoneBanner: React.FC<InTheZoneBannerProps> = ({ runningNumber }) => {
@ -47,7 +47,7 @@ const InTheZoneBanner: React.FC<InTheZoneBannerProps> = ({ runningNumber }) => {
{runningNumber}
</Box>
</Flex>
)
}
);
};
export default InTheZoneBanner
export default InTheZoneBanner;

View File

@ -1,47 +1,47 @@
import React, { useState, useContext } from "react"
import Modal from "../elements/Modal"
import { useMutation } from "@apollo/react-hooks"
import { useMutation } from "@apollo/client";
import {
useToast,
FormControl,
FormLabel,
RadioGroup,
Radio,
Flex,
Box,
FormErrorMessage,
CheckboxGroup,
Checkbox,
} from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import TextArea from "../elements/TextArea"
import Button from "../elements/Button"
import { FreeAgentPost } from "../../types"
CheckboxGroup,
Flex,
FormControl,
FormErrorMessage,
FormLabel,
Radio,
RadioGroup,
useToast,
} from "@chakra-ui/core";
import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import {
AddFreeAgentPostVars,
ADD_FREE_AGENT_POST,
} from "../../graphql/mutations/addFreeAgentPost"
import { FREE_AGENT_POSTS } from "../../graphql/queries/freeAgentPosts"
import { UPDATE_FREE_AGENT_POST } from "../../graphql/mutations/updateFreeAgentPost"
import { HIDE_FREE_AGENT_POST } from "../../graphql/mutations/hideFreeAgentPost"
import Alert from "../elements/Alert"
import { FREE_AGENT_MATCHES } from "../../graphql/queries/freeAgentMatches"
import { useTranslation } from "react-i18next"
} from "../../graphql/mutations/addFreeAgentPost";
import { HIDE_FREE_AGENT_POST } from "../../graphql/mutations/hideFreeAgentPost";
import { UPDATE_FREE_AGENT_POST } from "../../graphql/mutations/updateFreeAgentPost";
import { FREE_AGENT_MATCHES } from "../../graphql/queries/freeAgentMatches";
import { FREE_AGENT_POSTS } from "../../graphql/queries/freeAgentPosts";
import MyThemeContext from "../../themeContext";
import { FreeAgentPost } from "../../types";
import Alert from "../elements/Alert";
import Button from "../elements/Button";
import Modal from "../elements/Modal";
import TextArea from "../elements/TextArea";
interface FAPostModalProps {
closeModal: () => void
post?: FreeAgentPost
closeModal: () => void;
post?: FreeAgentPost;
}
const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
const { t } = useTranslation()
const { t } = useTranslation();
const [form, setForm] = useState<Partial<AddFreeAgentPostVars>>(
post ? post : {}
)
const [showErrors, setShowErrors] = useState(false)
const [deleting, setDeleting] = useState(false)
const toast = useToast()
const { themeColor, grayWithShade } = useContext(MyThemeContext)
);
const [showErrors, setShowErrors] = useState(false);
const [deleting, setDeleting] = useState(false);
const toast = useToast();
const { themeColor, grayWithShade } = useContext(MyThemeContext);
const [addFreeAgentPost, { loading }] = useMutation<
boolean,
@ -49,13 +49,13 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
>(ADD_FREE_AGENT_POST, {
variables: { ...(form as AddFreeAgentPostVars) },
onCompleted: (data) => {
closeModal()
closeModal();
toast({
description: t("freeagents;Free agent post added"),
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -64,10 +64,10 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [{ query: FREE_AGENT_POSTS }],
})
});
const [editFreeAgentPost, { loading: editLoading }] = useMutation<
boolean,
@ -75,13 +75,13 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
>(UPDATE_FREE_AGENT_POST, {
variables: { ...(form as AddFreeAgentPostVars) },
onCompleted: (data) => {
closeModal()
closeModal();
toast({
description: t("freeagents;Free agent post edited"),
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -90,10 +90,10 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [{ query: FREE_AGENT_POSTS }],
})
});
const [hideFreeAgentPost, { loading: hideLoading }] = useMutation<
boolean,
@ -101,13 +101,13 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
>(HIDE_FREE_AGENT_POST, {
variables: { ...(form as AddFreeAgentPostVars) },
onCompleted: (data) => {
closeModal()
closeModal();
toast({
description: t("freeagents;Free agent post deleted"),
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -116,19 +116,19 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [
{ query: FREE_AGENT_POSTS },
{ query: FREE_AGENT_MATCHES },
],
})
});
const handleChange = (newValueObject: Partial<AddFreeAgentPostVars>) => {
setForm({ ...form, ...newValueObject })
}
setForm({ ...form, ...newValueObject });
};
const actionType = post ? "EDIT" : "NEW"
const actionType = post ? "EDIT" : "NEW";
const handleSubmit = () => {
if (
@ -139,13 +139,13 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
(form.looking_for ?? "").length > 100 ||
(form.description ?? "").length > 1000
) {
setShowErrors(true)
return
setShowErrors(true);
return;
}
if (actionType === "NEW") addFreeAgentPost()
else if (actionType === "EDIT") editFreeAgentPost()
}
if (actionType === "NEW") addFreeAgentPost();
else if (actionType === "EDIT") editFreeAgentPost();
};
return (
<Modal
@ -326,7 +326,7 @@ const FAPostModal: React.FC<FAPostModalProps> = ({ closeModal, post }) => {
</Button>
</Flex>
</Modal>
)
}
);
};
export default FAPostModal
export default FAPostModal;

View File

@ -1,73 +1,73 @@
import { useMutation, useQuery } from "@apollo/react-hooks"
import { Box, Flex, Heading, IconButton, Image } from "@chakra-ui/core"
import { Link } from "@reach/router"
import React, { useContext, useState } from "react"
import { useTranslation } from "react-i18next"
import { FaMinus, FaPlus, FaTwitter } from "react-icons/fa"
import top500 from "../../assets/top500.png"
import { ADD_LIKE } from "../../graphql/mutations/addLike"
import { DELETE_LIKE } from "../../graphql/mutations/deleteLike"
import { FREE_AGENT_MATCHES } from "../../graphql/queries/freeAgentMatches"
import { USER } from "../../graphql/queries/user"
import MyThemeContext from "../../themeContext"
import { FreeAgentPost, UserData } from "../../types"
import Flag from "../common/Flag"
import Section from "../common/Section"
import UserAvatar from "../common/UserAvatar"
import WeaponImage from "../common/WeaponImage"
import Heart from "./Heart"
import RoleIcons from "./RoleIcons"
import VCIcon from "./VCIcon"
import { useMutation, useQuery } from "@apollo/client";
import { Box, Flex, Heading, IconButton, Image } from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import { FaMinus, FaPlus, FaTwitter } from "react-icons/fa";
import top500logo from "../../assets/top500.png";
import { ADD_LIKE } from "../../graphql/mutations/addLike";
import { DELETE_LIKE } from "../../graphql/mutations/deleteLike";
import { FREE_AGENT_MATCHES } from "../../graphql/queries/freeAgentMatches";
import { USER } from "../../graphql/queries/user";
import MyThemeContext from "../../themeContext";
import { FreeAgentPost, UserData } from "../../types";
import Flag from "../common/Flag";
import Section from "../common/Section";
import UserAvatar from "../common/UserAvatar";
import WeaponImage from "../common/WeaponImage";
import Heart from "./Heart";
import RoleIcons from "./RoleIcons";
import VCIcon from "./VCIcon";
interface FreeAgentCardProps {
post: FreeAgentPost
canLike: boolean
likedUsersIds: string[]
post: FreeAgentPost;
canLike: boolean;
likedUsersIds: string[];
}
const hasExtraInfo = (post: FreeAgentPost) => {
const { activity, description, looking_for, past_experience } = post
const { activity, description, looking_for, past_experience } = post;
if (!activity && !description && !looking_for && !past_experience) {
return false
return false;
}
return true
}
return true;
};
const FreeAgentCard: React.FC<FreeAgentCardProps> = ({
post,
canLike,
likedUsersIds,
}) => {
const [expanded, setExpanded] = useState(false)
const { grayWithShade, themeColorWithShade } = useContext(MyThemeContext)
const { t, i18n } = useTranslation()
const { discord_user } = post
const canBeExpanded = hasExtraInfo(post)
const [expanded, setExpanded] = useState(false);
const { grayWithShade, themeColorWithShade } = useContext(MyThemeContext);
const { t, i18n } = useTranslation();
const { discord_user } = post;
const canBeExpanded = hasExtraInfo(post);
const { data } = useQuery<UserData>(USER)
const { data } = useQuery<UserData>(USER);
const [addLike, { loading: likeLoading }] = useMutation<
{ addLike: boolean },
{ discord_id: string }
>(ADD_LIKE, {
refetchQueries: [{ query: FREE_AGENT_MATCHES }],
})
});
const [deleteLike, { loading: deleteLoading }] = useMutation<
{ deleteLike: boolean },
{ discord_id: string }
>(DELETE_LIKE, {
refetchQueries: [{ query: FREE_AGENT_MATCHES }],
})
});
const liked = likedUsersIds.indexOf(discord_user.discord_id) !== -1
const liked = likedUsersIds.indexOf(discord_user.discord_id) !== -1;
const handleHeartClick = () => {
if (liked)
deleteLike({ variables: { discord_id: discord_user.discord_id } })
else addLike({ variables: { discord_id: discord_user.discord_id } })
}
deleteLike({ variables: { discord_id: discord_user.discord_id } });
else addLike({ variables: { discord_id: discord_user.discord_id } });
};
return (
<Section
@ -83,7 +83,7 @@ const FreeAgentCard: React.FC<FreeAgentCardProps> = ({
<Box width="50px" m="1em">
{discord_user.top500 && (
<Image
src={top500}
src={top500logo}
alt={t("freeagents;Free agent has reached Top 500 in X Rank")}
height="40px"
width="auto"
@ -210,7 +210,7 @@ const FreeAgentCard: React.FC<FreeAgentCardProps> = ({
</Box>
)}
</Section>
)
}
);
};
export default FreeAgentCard
export default FreeAgentCard;

View File

@ -1,78 +1,78 @@
import React, { useState } from "react"
import { useQuery } from "@apollo/react-hooks"
import { USER } from "../../graphql/queries/user"
import {
UserData,
FreeAgentPostsData,
Weapon,
FreeAgentPost,
} 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 Posts from "./Posts"
import PageHeader from "../common/PageHeader"
import { Helmet } from "react-helmet-async"
import WeaponSelector from "../common/WeaponSelector"
import RadioGroup from "../elements/RadioGroup"
import { continents } from "../../utils/lists"
import { Collapse, Flex, Box } from "@chakra-ui/core"
import Button from "../elements/Button"
import { FaFilter } from "react-icons/fa"
import FAPostModal from "./FAPostModal"
import { useQuery } from "@apollo/client";
import { Box, Collapse, Flex } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import React, { useState } from "react";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import { FaFilter } from "react-icons/fa";
import {
FreeAgentMatchesData,
FREE_AGENT_MATCHES,
} from "../../graphql/queries/freeAgentMatches"
import Matches from "./Matches"
import Alert from "../elements/Alert"
import { useTranslation } from "react-i18next"
} from "../../graphql/queries/freeAgentMatches";
import { FREE_AGENT_POSTS } from "../../graphql/queries/freeAgentPosts";
import { USER } from "../../graphql/queries/user";
import {
FreeAgentPost,
FreeAgentPostsData,
UserData,
Weapon,
} from "../../types";
import { continents } from "../../utils/lists";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import WeaponSelector from "../common/WeaponSelector";
import Alert from "../elements/Alert";
import Button from "../elements/Button";
import RadioGroup from "../elements/RadioGroup";
import FAPostModal from "./FAPostModal";
import Matches from "./Matches";
import Posts from "./Posts";
const playstyleToEnum = {
"Frontline/Slayer": "FRONTLINE",
"Midline/Support": "MIDLINE",
"Backline/Anchor": "BACKLINE",
} as const
} as const;
const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
const { t } = useTranslation()
const [weapon, setWeapon] = useState<Weapon | null>(null)
const [showFilters, setShowFilters] = useState(false)
const [showModal, setShowModal] = useState(false)
const { t } = useTranslation();
const [weapon, setWeapon] = useState<Weapon | null>(null);
const [showFilters, setShowFilters] = useState(false);
const [showModal, setShowModal] = useState(false);
const [playstyle, setPlaystyle] = useState<
"Any" | "Frontline/Slayer" | "Midline/Support" | "Backline/Anchor"
>("Any")
>("Any");
const [region, setRegion] = useState<
"Any" | "Europe" | "The Americas" | "Oceania" | "Other"
>("Any")
>("Any");
const { data, error, loading } = useQuery<FreeAgentPostsData>(
FREE_AGENT_POSTS
)
);
const {
data: userData,
error: userQueryError,
loading: userQueryLoading,
} = useQuery<UserData>(USER)
} = useQuery<UserData>(USER);
const { data: matchesData, error: matchesError } = useQuery<
FreeAgentMatchesData
>(FREE_AGENT_MATCHES)
>(FREE_AGENT_MATCHES);
if (error) return <Error errorMessage={error.message} />
if (userQueryError) return <Error errorMessage={userQueryError.message} />
if (matchesError) return <Error errorMessage={matchesError.message} />
if (loading || userQueryLoading) return <Loading />
if (error) return <Error errorMessage={error.message} />;
if (userQueryError) return <Error errorMessage={userQueryError.message} />;
if (matchesError) return <Error errorMessage={matchesError.message} />;
if (loading || userQueryLoading) return <Loading />;
const faPosts = data!.freeAgentPosts
const faPosts = data!.freeAgentPosts;
const ownFAPost = faPosts.find(
(post) => post.discord_user.discord_id === userData!.user?.discord_id
)
);
const buttonText =
ownFAPost && !ownFAPost.hidden
? t("freeagents;Edit free agent post")
: t("freeagents;New free agent post")
: t("freeagents;New free agent post");
const altWeaponMap = new Map([
["Splattershot", "Hero Shot Replica"],
@ -85,12 +85,12 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
["Heavy Splatling", "Hero Splatling Replica"],
["Splat Dualies", "Hero Dualie Replicas"],
["Splat Brella", "Hero Brella Replica"],
])
]);
const postsFilter = (post: FreeAgentPost) => {
if (post.hidden) return false
if (post.hidden) return false;
const usersWeapons = post.discord_user.weapons ?? []
const usersWeapons = post.discord_user.weapons ?? [];
if (
weapon &&
@ -99,31 +99,31 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
usersWeapons.includes(altWeaponMap.get(weapon) as any)
)
) {
return false
return false;
}
if (playstyle !== "Any") {
if (post.playstyles.indexOf(playstyleToEnum[playstyle]) === -1)
return false
return false;
}
if (region !== "Any") {
if (!post.discord_user.country) {
if (region === "Other") return true
if (region === "Other") return true;
return false
return false;
}
const continentCode = continents[post.discord_user.country]
const continentCode = continents[post.discord_user.country];
if (region === "Europe" && continentCode !== "EU") return false
if (region === "Europe" && continentCode !== "EU") return false;
else if (
region === "The Americas" &&
continentCode !== "NA" &&
continentCode !== "SA"
)
return false
else if (region === "Oceania" && continentCode !== "OC") return false
return false;
else if (region === "Oceania" && continentCode !== "OC") return false;
else if (
region === "Other" &&
continentCode !== "AF" &&
@ -131,11 +131,11 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
continentCode !== "AS" &&
continentCode !== "OC"
)
return false
return false;
}
return true
}
return true;
};
const getTopRightContent = () => {
if (!userData!.user)
@ -145,10 +145,10 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
{t("freeagents;loginPrompt")}
</Alert>
</Box>
)
);
if (ownFAPost && ownFAPost.hidden) {
const weekFromCreatingFAPost = parseInt(ownFAPost.createdAt) + 604800000
const weekFromCreatingFAPost = parseInt(ownFAPost.createdAt) + 604800000;
if (weekFromCreatingFAPost > Date.now()) {
return (
<Box maxW="300px">
@ -156,7 +156,7 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
{t("freeagents;pleaseWaitPrompt")}
</Alert>
</Box>
)
);
}
}
@ -164,8 +164,8 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
<Box m="0.5em">
<Button onClick={() => setShowModal(true)}>{buttonText}</Button>
</Box>
)
}
);
};
return (
<>
@ -256,7 +256,7 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
}
/>
</>
)
}
);
};
export default FreeAgentsPage
export default FreeAgentsPage;

View File

@ -1,14 +1,14 @@
import React, { useContext } from "react"
import IconButton from "../elements/IconButton"
import { FaRegHeart, FaHeart } from "react-icons/fa"
import { Popover, PopoverTrigger, PopoverContent, Flex } from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import React, { useContext } from "react";
import IconButton from "../elements/IconButton";
import { FaRegHeart, FaHeart } from "react-icons/fa";
import { Popover, PopoverTrigger, PopoverContent, Flex } from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
interface HeartProps {
disabled: boolean
loading: boolean
active: boolean
onClick: () => void
disabled: boolean;
loading: boolean;
active: boolean;
onClick: () => void;
}
const Heart: React.FC<HeartProps> = ({
@ -17,15 +17,15 @@ const Heart: React.FC<HeartProps> = ({
loading,
onClick,
}) => {
const { darkerBgColor } = useContext(MyThemeContext)
const { darkerBgColor } = useContext(MyThemeContext);
const getPopoverContent = () => {
if (active)
return "You have liked this free agent! If they also give you a like match will be shown on the top."
return "You have liked this free agent! If they also give you a like match will be shown on the top.";
if (disabled)
return "Make your own free agent post to like and have a chance to match up with this player!"
return "If you like what you see give this free agent a like and see if they want to team up with you as well!"
}
return "Make your own free agent post to like and have a chance to match up with this player!";
return "If you like what you see give this free agent a like and see if they want to team up with you as well!";
};
return (
<>
@ -47,7 +47,7 @@ const Heart: React.FC<HeartProps> = ({
</PopoverContent>
</Popover>
</>
)
}
);
};
export default Heart
export default Heart;

View File

@ -1,5 +1,5 @@
import React, { useContext } from "react"
import FieldsetWithLegend from "../common/FieldsetWithLegend"
import React, { useContext } from "react";
import FieldsetWithLegend from "../common/FieldsetWithLegend";
import {
Flex,
Box,
@ -9,27 +9,27 @@ import {
PopoverArrow,
AvatarGroup,
Avatar,
} from "@chakra-ui/core"
import UserAvatar from "../common/UserAvatar"
import MyThemeContext from "../../themeContext"
import { useTranslation, Trans } from "react-i18next"
} from "@chakra-ui/core";
import UserAvatar from "../common/UserAvatar";
import MyThemeContext from "../../themeContext";
import { useTranslation, Trans } from "react-i18next";
interface MatchesProps {
matches: {
username: string
discriminator: string
avatar?: string
}[]
likesReceived: number
username: string;
discriminator: string;
avatar?: string;
}[];
likesReceived: number;
}
const Matches: React.FC<MatchesProps> = ({ matches, likesReceived }) => {
const { t } = useTranslation()
const { grayWithShade, darkerBgColor } = useContext(MyThemeContext)
const { t } = useTranslation();
const { grayWithShade, darkerBgColor } = useContext(MyThemeContext);
if (matches.length === 0 && likesReceived === 0) return null
if (matches.length === 0 && likesReceived === 0) return null;
const unrequitedLove = likesReceived - matches.length
const unrequitedLove = likesReceived - matches.length;
return (
<Flex justifyContent="center">
@ -42,7 +42,7 @@ const Matches: React.FC<MatchesProps> = ({ matches, likesReceived }) => {
<>
<Flex justifyContent="center" flexWrap="wrap">
{matches.map((match) => {
const matchFullName = `${match.username}#${match.discriminator}`
const matchFullName = `${match.username}#${match.discriminator}`;
return (
<Box
key={`${match.username}${match.discriminator}`}
@ -68,7 +68,7 @@ const Matches: React.FC<MatchesProps> = ({ matches, likesReceived }) => {
</PopoverContent>
</Popover>
</Box>
)
);
})}
</Flex>
</>
@ -91,7 +91,7 @@ const Matches: React.FC<MatchesProps> = ({ matches, likesReceived }) => {
)}
</FieldsetWithLegend>
</Flex>
)
}
);
};
export default Matches
export default Matches;

View File

@ -1,16 +1,16 @@
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"
import { useTranslation } from "react-i18next"
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";
import { useTranslation } from "react-i18next";
interface PostsAccordionProps {
posts: FreeAgentPost[]
canLike: boolean
likedUsersIds: string[]
posts: FreeAgentPost[];
canLike: boolean;
likedUsersIds: string[];
}
const Posts: React.FC<PostsAccordionProps> = ({
@ -18,15 +18,15 @@ const Posts: React.FC<PostsAccordionProps> = ({
canLike,
likedUsersIds,
}) => {
const { t } = useTranslation()
const [agentsToShow, setAgentsToShow] = useState(5)
const { t } = useTranslation();
const [agentsToShow, setAgentsToShow] = useState(5);
if (posts.length === 0) {
return (
<Alert status="info">
{t("freeagents;No free agents found with the current filter")}
</Alert>
)
);
}
return (
<>
@ -60,7 +60,7 @@ const Posts: React.FC<PostsAccordionProps> = ({
</Box>
</Box>
</>
)
}
);
};
export default Posts
export default Posts;

View File

@ -1,14 +1,14 @@
import React from "react"
import { Flex, Box } from "@chakra-ui/core"
import { FaCrosshairs, FaBriefcaseMedical, FaAnchor } from "react-icons/fa"
import { useTranslation } from "react-i18next"
import React from "react";
import { Flex, Box } from "@chakra-ui/core";
import { FaCrosshairs, FaBriefcaseMedical, FaAnchor } from "react-icons/fa";
import { useTranslation } from "react-i18next";
interface RoleIconsProps {
playstyles: ("FRONTLINE" | "MIDLINE" | "BACKLINE")[]
playstyles: ("FRONTLINE" | "MIDLINE" | "BACKLINE")[];
}
const RoleIcons: React.FC<RoleIconsProps> = ({ playstyles }) => {
const { t } = useTranslation()
const { t } = useTranslation();
return (
<Flex>
<Box
@ -37,7 +37,7 @@ const RoleIcons: React.FC<RoleIconsProps> = ({ playstyles }) => {
cursor="help"
/>
</Flex>
)
}
);
};
export default RoleIcons
export default RoleIcons;

View File

@ -1,10 +1,10 @@
import React from "react"
import { Box } from "@chakra-ui/core"
import { FaMicrophone } from "react-icons/fa"
import { useTranslation } from "react-i18next"
import React from "react";
import { Box } from "@chakra-ui/core";
import { FaMicrophone } from "react-icons/fa";
import { useTranslation } from "react-i18next";
interface VCIconProps {
canVC: "YES" | "USUALLY" | "SOMETIMES" | "NO"
canVC: "YES" | "USUALLY" | "SOMETIMES" | "NO";
}
const color = {
@ -12,17 +12,17 @@ const color = {
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 }) => {
const { t } = useTranslation()
const { t } = useTranslation();
return (
<Box
as={FaMicrophone}
@ -32,7 +32,7 @@ const VCIcon: React.FC<VCIconProps> = ({ canVC }) => {
h="auto"
cursor="help"
/>
)
}
);
};
export default VCIcon
export default VCIcon;

View File

@ -1,18 +1,18 @@
import React from "react"
import { RouteComponentProps } from "@reach/router"
import { Image, Heading, Flex, Box } from "@chakra-ui/core"
import { posterGirl } from "../../assets/imageImports"
import { useContext } from "react"
import MyThemeContext from "../../themeContext"
import "./HomePage.css"
import { Helmet } from "react-helmet-async"
import Stats from "./Stats"
import WeeksTournaments from "./WeeksTournaments"
import { useTranslation } from "react-i18next"
import React from "react";
import { RouteComponentProps } from "@reach/router";
import { Image, Heading, Flex, Box } from "@chakra-ui/core";
import { posterGirl } from "../../assets/imageImports";
import { useContext } from "react";
import MyThemeContext from "../../themeContext";
import "./HomePage.css";
import { Helmet } from "react-helmet-async";
import Stats from "./Stats";
import WeeksTournaments from "./WeeksTournaments";
import { useTranslation } from "react-i18next";
const HomePage: React.FC<RouteComponentProps> = () => {
const { colorMode, grayWithShade } = useContext(MyThemeContext)
const { t } = useTranslation()
const { colorMode, grayWithShade } = useContext(MyThemeContext);
const { t } = useTranslation();
return (
<>
<Helmet>
@ -42,7 +42,7 @@ const HomePage: React.FC<RouteComponentProps> = () => {
<WeeksTournaments />
</Box>
</>
)
}
);
};
export default HomePage
export default HomePage;

View File

@ -1,38 +1,38 @@
import { useQuery } from "@apollo/react-hooks"
import { Box, Flex, Skeleton } from "@chakra-ui/core"
import { Link } from "@reach/router"
import React, { useContext } from "react"
import { STATS } from "../../graphql/queries/stats"
import MyThemeContext from "../../themeContext"
import Error from "../common/Error"
import { useTranslation, Trans } from "react-i18next"
import { useQuery } from "@apollo/client";
import { Box, Flex, Skeleton } from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext } from "react";
import { Trans, useTranslation } from "react-i18next";
import { STATS } from "../../graphql/queries/stats";
import MyThemeContext from "../../themeContext";
import Error from "../common/Error";
interface StatsData {
stats: {
build_count: number
tournament_count: number
fa_count: number
user_count: number
}
build_count: number;
tournament_count: number;
fa_count: number;
user_count: number;
};
}
const xRankMonths = () => {
const date = new Date()
const fullYears = date.getFullYear() - 2019
return 8 + fullYears * 12 + date.getMonth()
}
const date = new Date();
const fullYears = date.getFullYear() - 2019;
return 8 + fullYears * 12 + date.getMonth();
};
const Stats: React.FC = () => {
const { grayWithShade, themeColorWithShade } = useContext(MyThemeContext)
const { data, error } = useQuery<StatsData>(STATS)
const { t } = useTranslation()
const { grayWithShade, themeColorWithShade } = useContext(MyThemeContext);
const { data, error } = useQuery<StatsData>(STATS);
const { t } = useTranslation();
if (error) return <Error errorMessage={error.message} />
if (error) return <Error errorMessage={error.message} />;
const stats = data?.stats
const stats = data?.stats;
interface StatOrSkeletonProps {
value?: number
value?: number;
}
const StatOrSkeleton: React.FC<StatOrSkeletonProps> = ({ value }) => {
@ -44,12 +44,12 @@ const Stats: React.FC = () => {
display="inline-block"
mr="0.2em"
/>
)
);
}
return <>{value}</>
}
return <>{value}</>;
};
const xRankMonthCount = xRankMonths()
const xRankMonthCount = xRankMonths();
return (
<>
@ -157,7 +157,7 @@ const Stats: React.FC = () => {
</Box>
</Flex>
</>
)
}
);
};
export default Stats
export default Stats;

View File

@ -1,35 +1,37 @@
import React, { useContext } from "react"
import Loading from "../common/Loading"
import Error from "../common/Error"
import { useQuery } from "@apollo/react-hooks"
import { useQuery } from "@apollo/client";
import { Box, Flex, Heading } from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext } from "react";
import { useTranslation } from "react-i18next";
import { FiClock, FiInfo } from "react-icons/fi";
import {
UpcomingEventsData,
UPCOMING_EVENTS,
} from "../../graphql/queries/upcomingEvents"
import { getWeek } from "../../utils/helperFunctions"
import SubHeader from "../common/SubHeader"
import { Heading, Flex, Box } from "@chakra-ui/core"
import { FiClock, FiInfo } from "react-icons/fi"
import MyThemeContext from "../../themeContext"
import Button from "../elements/Button"
import { Link } from "@reach/router"
import { useTranslation } from "react-i18next"
} from "../../graphql/queries/upcomingEvents";
import MyThemeContext from "../../themeContext";
import { getWeek } from "../../utils/helperFunctions";
import Error from "../common/Error";
import Loading from "../common/Loading";
import SubHeader from "../common/SubHeader";
import Button from "../elements/Button";
const WeeksTournaments: React.FC = () => {
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext)
const { data, error, loading } = useQuery<UpcomingEventsData>(UPCOMING_EVENTS)
const { t } = useTranslation()
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext);
const { data, error, loading } = useQuery<UpcomingEventsData>(
UPCOMING_EVENTS
);
const { t } = useTranslation();
if (loading) return <Loading />
if (error) return <Error errorMessage={error.message} />
if (loading) return <Loading />;
if (error) return <Error errorMessage={error.message} />;
const thisWeekNumber = getWeek(new Date())
const thisWeekNumber = getWeek(new Date());
const events = data!.upcomingEvents.filter(
(event) => getWeek(new Date(parseInt(event.date))) === thisWeekNumber
)
);
if (events.length === 0) return null
if (events.length === 0) return null;
return (
<>
@ -63,7 +65,7 @@ const WeeksTournaments: React.FC = () => {
</Box>
</Flex>
</>
)
}
);
};
export default WeeksTournaments
export default WeeksTournaments;

View File

@ -1,18 +1,18 @@
import { Box, Collapse, Flex, Link } from "@chakra-ui/core"
import { RouteComponentProps } from "@reach/router"
import React, { useContext, useState } from "react"
import MyThemeContext from "../../themeContext"
import { abilitiesGameOrder, gearCodes, weaponCodes } from "../../utils/lists"
import PageHeader from "../common/PageHeader"
import WeaponImage from "../common/WeaponImage"
import Button from "../elements/Button"
import Emoji from "../elements/Emoji"
import { Box, Collapse, Flex, Link } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import React, { useContext, useState } from "react";
import MyThemeContext from "../../themeContext";
import { abilitiesGameOrder, gearCodes, weaponCodes } from "../../utils/lists";
import PageHeader from "../common/PageHeader";
import WeaponImage from "../common/WeaponImage";
import Button from "../elements/Button";
import Emoji from "../elements/Emoji";
const MarkdownHelpPage: React.FC<RouteComponentProps> = () => {
const { themeColorWithShade } = useContext(MyThemeContext)
const [showWeapons, setShowWeapons] = useState(false)
const [showAbilities, setShowAbilities] = useState(false)
const [showGear, setShowGear] = useState(false)
const { themeColorWithShade } = useContext(MyThemeContext);
const [showWeapons, setShowWeapons] = useState(false);
const [showAbilities, setShowAbilities] = useState(false);
const [showGear, setShowGear] = useState(false);
return (
<>
<PageHeader title="Markdown Help" />
@ -139,7 +139,7 @@ const MarkdownHelpPage: React.FC<RouteComponentProps> = () => {
</p>
</Box>
</>
)
}
);
};
export default MarkdownHelpPage
export default MarkdownHelpPage;

View File

@ -1,7 +1,7 @@
import React, { useState, useContext } from "react"
import Draggable from "react-draggable"
import { Tools } from "@sendou/react-sketch"
import { useHotkeys } from "react-hotkeys-hook"
import React, { useState, useContext } from "react";
import Draggable from "react-draggable";
import { Tools } from "@sendou/react-sketch";
import { useHotkeys } from "react-hotkeys-hook";
import {
Box,
Flex,
@ -10,7 +10,7 @@ import {
PopoverTrigger,
PopoverContent,
PopoverBody,
} from "@chakra-ui/core"
} from "@chakra-ui/core";
import {
FaPencilAlt,
FaRegSquare,
@ -20,22 +20,22 @@ import {
FaRedo,
FaUndo,
FaFont,
} from "react-icons/fa"
import { AiOutlineLine } from "react-icons/ai"
import MyThemeContext from "../../themeContext"
import { CirclePicker, ColorResult } from "react-color"
import { useTranslation } from "react-i18next"
} from "react-icons/fa";
import { AiOutlineLine } from "react-icons/ai";
import MyThemeContext from "../../themeContext";
import { CirclePicker, ColorResult } from "react-color";
import { useTranslation } from "react-i18next";
interface PlannerColorPickerProps {
color: string
setColor: (newColor: string) => void
color: string;
setColor: (newColor: string) => void;
}
const PlannerColorPicker: React.FC<PlannerColorPickerProps> = ({
color,
setColor,
}) => {
const { darkerBgColor } = useContext(MyThemeContext)
const { darkerBgColor } = useContext(MyThemeContext);
return (
<Popover placement="bottom">
<PopoverTrigger>
@ -59,21 +59,21 @@ const PlannerColorPicker: React.FC<PlannerColorPickerProps> = ({
</PopoverBody>
</PopoverContent>
</Popover>
)
}
);
};
interface DraggableToolsSelectorProps {
tool: any
setTool: React.Dispatch<any>
redo: () => void
redoIsDisabled: boolean
undo: () => void
undoIsDisabled: boolean
removeSelected: () => void
removeIsDisabled: boolean
addText: () => void
color: string
setColor: (newColor: string) => void
tool: any;
setTool: React.Dispatch<any>;
redo: () => void;
redoIsDisabled: boolean;
undo: () => void;
undoIsDisabled: boolean;
removeSelected: () => void;
removeIsDisabled: boolean;
addText: () => void;
color: string;
setColor: (newColor: string) => void;
}
const DraggableToolsSelector: React.FC<DraggableToolsSelectorProps> = ({
@ -89,22 +89,22 @@ const DraggableToolsSelector: React.FC<DraggableToolsSelectorProps> = ({
color,
setColor,
}) => {
const { darkerBgColor, themeColorHex } = useContext(MyThemeContext)
const { t } = useTranslation()
const [activeDrags, setActiveDrags] = useState(0)
useHotkeys("p", () => setTool(Tools.Pencil))
useHotkeys("l", () => setTool(Tools.Line))
useHotkeys("r", () => setTool(Tools.Rectangle))
useHotkeys("c", () => setTool(Tools.Circle))
useHotkeys("s", () => setTool(Tools.Select))
const { darkerBgColor, themeColorHex } = useContext(MyThemeContext);
const { t } = useTranslation();
const [activeDrags, setActiveDrags] = useState(0);
useHotkeys("p", () => setTool(Tools.Pencil));
useHotkeys("l", () => setTool(Tools.Line));
useHotkeys("r", () => setTool(Tools.Rectangle));
useHotkeys("c", () => setTool(Tools.Circle));
useHotkeys("s", () => setTool(Tools.Select));
const onStart = () => {
setActiveDrags(activeDrags + 1)
}
setActiveDrags(activeDrags + 1);
};
const onStop = () => {
setActiveDrags(activeDrags - 1)
}
setActiveDrags(activeDrags - 1);
};
return (
<Draggable handle="strong" onStart={onStart} onStop={onStop}>
@ -218,7 +218,7 @@ const DraggableToolsSelector: React.FC<DraggableToolsSelectorProps> = ({
</Flex>
</Box>
</Draggable>
)
}
);
};
export default DraggableToolsSelector
export default DraggableToolsSelector;

View File

@ -1,30 +1,30 @@
import { Box, Flex } from "@chakra-ui/core"
import React, { useContext, useState } from "react"
import Draggable from "react-draggable"
import { useTranslation } from "react-i18next"
import MyThemeContext from "../../themeContext"
import { Weapon } from "../../types"
import { weapons } from "../../utils/lists"
import WeaponImage from "../common/WeaponImage"
import { Box, Flex } from "@chakra-ui/core";
import React, { useContext, useState } from "react";
import Draggable from "react-draggable";
import { useTranslation } from "react-i18next";
import MyThemeContext from "../../themeContext";
import { Weapon } from "../../types";
import { weapons } from "../../utils/lists";
import WeaponImage from "../common/WeaponImage";
interface DraggableWeaponSelectorProps {
addWeaponImage: (weapon: Weapon) => void
addWeaponImage: (weapon: Weapon) => void;
}
const DraggableWeaponSelector: React.FC<DraggableWeaponSelectorProps> = ({
addWeaponImage,
}) => {
const { darkerBgColor } = useContext(MyThemeContext)
const { t } = useTranslation()
const [activeDrags, setActiveDrags] = useState(0)
const { darkerBgColor } = useContext(MyThemeContext);
const { t } = useTranslation();
const [activeDrags, setActiveDrags] = useState(0);
const onStart = () => {
setActiveDrags(activeDrags + 1)
}
setActiveDrags(activeDrags + 1);
};
const onStop = () => {
setActiveDrags(activeDrags - 1)
}
setActiveDrags(activeDrags - 1);
};
return (
<Draggable handle="strong" onStart={onStart} onStop={onStop}>
@ -59,7 +59,7 @@ const DraggableWeaponSelector: React.FC<DraggableWeaponSelectorProps> = ({
</Box>
</Box>
</Draggable>
)
}
);
};
export default DraggableWeaponSelector
export default DraggableWeaponSelector;

View File

@ -1,36 +1,36 @@
import { Box, Flex } from "@chakra-ui/core"
import { RouteComponentProps } from "@reach/router"
import { SketchField, Tools } from "@sendou/react-sketch"
import React, { useEffect, useLayoutEffect, useRef, useState } from "react"
import { Helmet } from "react-helmet-async"
import { useTranslation } from "react-i18next"
import { Box, Flex } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import { SketchField, Tools } from "@sendou/react-sketch";
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import {
FaBomb,
FaFileDownload,
FaFileImage,
FaFileUpload,
} from "react-icons/fa"
import useBreakPoints from "../../hooks/useBreakPoints"
import { Stage, Weapon } from "../../types"
import english_internal from "../../utils/english_internal.json"
import Error from "../common/Error"
import PageHeader from "../common/PageHeader"
import Button from "../elements/Button"
import DraggableToolsSelector from "./DraggableToolsSelector"
import DraggableWeaponSelector from "./DraggableWeaponSelector"
import MapSelect from "./MapSelect"
} from "react-icons/fa";
import useBreakPoints from "../../hooks/useBreakPoints";
import { Stage, Weapon } from "../../types";
import english_internal from "../../utils/english_internal.json";
import Error from "../common/Error";
import PageHeader from "../common/PageHeader";
import Button from "../elements/Button";
import DraggableToolsSelector from "./DraggableToolsSelector";
import DraggableWeaponSelector from "./DraggableWeaponSelector";
import MapSelect from "./MapSelect";
export interface PlannerMapBg {
view: "M" | "R"
stage: Stage
mode: "SZ" | "TC" | "RM" | "CB"
view: "M" | "R";
stage: Stage;
mode: "SZ" | "TC" | "RM" | "CB";
}
const REEF = {
view: "M",
stage: "The Reef",
mode: "SZ",
} as const
} as const;
const reversedCodes = [
["Ancho-V Games", "AG"],
@ -56,9 +56,9 @@ const reversedCodes = [
["The Reef", "TR"],
["Wahoo World", "WH"],
["Walleye Warehouse", "WW"],
] as const
] as const;
const stageToCode = new Map<Stage, string>(reversedCodes)
const stageToCode = new Map<Stage, string>(reversedCodes);
const codes = [
["AG", "Ancho-V Games"],
@ -84,18 +84,18 @@ const codes = [
["TR", "The Reef"],
["WH", "Wahoo World"],
["WW", "Walleye Warehouse"],
] as const
] as const;
const codeToStage = new Map(codes)
const codeToStage = new Map(codes);
const plannerMapBgToImage = (bg: PlannerMapBg) =>
`${process.env.PUBLIC_URL}/plannerMaps/${bg.view} ${stageToCode.get(
bg.stage
)} ${bg.mode}.png`
)} ${bg.mode}.png`;
const MapPlannerPage: React.FC<RouteComponentProps> = () => {
let sketch: any = null
const isSmall = useBreakPoints(1127)
let sketch: any = null;
const isSmall = useBreakPoints(1127);
const defaultValue = {
shadowWidth: 0,
shadowOffset: 0,
@ -120,28 +120,28 @@ const MapPlannerPage: React.FC<RouteComponentProps> = () => {
expandImages: false,
expandControlled: false,
enableCopyPaste: false,
}
const fileInput = useRef<HTMLInputElement | null>(null)
const [tool, setTool] = useState(Tools.RectangleLabel)
const [color, setColor] = useState("#f44336")
const [canUndo, setCanUndo] = useState(false)
const [canRedo, setCanRedo] = useState(false)
const [bg, setBg] = useState<PlannerMapBg>(REEF)
const [controlledValue, setControlledValue] = useState(defaultValue)
const { t } = useTranslation()
};
const fileInput = useRef<HTMLInputElement | null>(null);
const [tool, setTool] = useState(Tools.RectangleLabel);
const [color, setColor] = useState("#f44336");
const [canUndo, setCanUndo] = useState(false);
const [canRedo, setCanRedo] = useState(false);
const [bg, setBg] = useState<PlannerMapBg>(REEF);
const [controlledValue, setControlledValue] = useState(defaultValue);
const { t } = useTranslation();
// doesn't work properly when coming back from another page - not sure why
useLayoutEffect(() => {
sketch.setBackgroundFromDataUrl(plannerMapBgToImage(REEF))
}, [sketch])
sketch.setBackgroundFromDataUrl(plannerMapBgToImage(REEF));
}, [sketch]);
const addImageToSketch = (weapon: Weapon) => {
if (!sketch) return
if (!sketch) return;
sketch.addImg(
`https://raw.githubusercontent.com/Leanny/leanny.github.io/master/splat2/weapons/Wst_${english_internal[weapon]}.png`
)
setTool(Tools.Select)
}
);
setTool(Tools.Select);
};
const addTextToSketch = () => {
sketch.addText("Double-click to edit", {
@ -150,111 +150,115 @@ const MapPlannerPage: React.FC<RouteComponentProps> = () => {
stroke: "#000000",
strokeWidth: 3,
paintFirst: "stroke",
})
setTool(Tools.Select)
}
});
setTool(Tools.Select);
};
const undo = () => {
sketch.undo()
setCanUndo(sketch.canUndo())
setCanRedo(sketch.canRedo())
}
sketch.undo();
setCanUndo(sketch.canUndo());
setCanRedo(sketch.canRedo());
};
const redo = () => {
sketch.redo()
setCanUndo(sketch.canUndo())
setCanRedo(sketch.canRedo())
}
sketch.redo();
setCanUndo(sketch.canUndo());
setCanRedo(sketch.canRedo());
};
const removeSelected = () => {
sketch.removeSelected()
}
sketch.removeSelected();
};
const onSketchChange = () => {
if (!sketch) return
let prev = canUndo
let now = sketch.canUndo()
if (!sketch) return;
let prev = canUndo;
let now = sketch.canUndo();
if (prev !== now) {
setCanUndo(now)
setCanUndo(now);
}
}
};
const getDateFormatted = () => {
const today = new Date()
const today = new Date();
const date =
today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate()
today.getFullYear() +
"-" +
(today.getMonth() + 1) +
"-" +
today.getDate();
const time =
today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds()
return date + " " + time
}
today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
return date + " " + time;
};
const download = (dataUrl: string, extension: string) => {
if (!bg) return
let a = document.createElement("a")
document.body.appendChild(a)
a.style.display = "none"
a.href = dataUrl
if (!bg) return;
let a = document.createElement("a");
document.body.appendChild(a);
a.style.display = "none";
a.href = dataUrl;
a.download = `${bg.view}-${stageToCode.get(bg.stage)}-${
bg.mode
} plans ${getDateFormatted()}.${extension}`
a.click()
window.URL.revokeObjectURL(dataUrl)
}
} plans ${getDateFormatted()}.${extension}`;
a.click();
window.URL.revokeObjectURL(dataUrl);
};
const handleUpload = () => {
if (!fileInput.current) {
return
return;
}
fileInput.current.click()
}
fileInput.current.click();
};
const parseAndSetForms = (name: string) => {
const firstPart = name.split(" ")[0]
if (!(firstPart.length === 7 || !firstPart.includes("-"))) return
const firstPart = name.split(" ")[0];
if (!(firstPart.length === 7 || !firstPart.includes("-"))) return;
const split = firstPart.split("-")
if (split.length !== 3) return
const split = firstPart.split("-");
if (split.length !== 3) return;
const [view, stage, mode] = split
const [view, stage, mode] = split;
if (!["M", "R"].includes(view)) return
if (!["M", "R"].includes(view)) return;
if (
!Array.from(reversedCodes.values())
.map((tuple) => tuple[1])
.includes(stage as any)
)
if (!["SZ", "TC", "RM", "CB"].includes(mode)) return
if (!["SZ", "TC", "RM", "CB"].includes(mode)) return;
setBg({
view: view as any,
stage: codeToStage.get(stage as any)!,
mode: mode as any,
})
}
});
};
const files = fileInput.current?.files
const files = fileInput.current?.files;
useEffect(() => {
if (!fileInput.current?.files?.length) return
if (!fileInput.current?.files?.length) return;
const fileObj = fileInput.current.files[0]
const reader = new FileReader()
const fileObj = fileInput.current.files[0];
const reader = new FileReader();
reader.onload = function (event) {
const jsonObj = JSON.parse(event.target!.result as any)
setControlledValue(jsonObj)
}
const jsonObj = JSON.parse(event.target!.result as any);
setControlledValue(jsonObj);
};
reader.readAsText(fileObj)
reader.readAsText(fileObj);
parseAndSetForms(fileObj.name)
}, [files])
parseAndSetForms(fileObj.name);
}, [files]);
useEffect(() => {
if (!sketch) return
setCanUndo(false)
sketch.setBackgroundFromDataUrl(plannerMapBgToImage(bg))
if (!sketch) return;
setCanUndo(false);
sketch.setBackgroundFromDataUrl(plannerMapBgToImage(bg));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [bg])
}, [bg]);
return (
<>
@ -297,8 +301,8 @@ const MapPlannerPage: React.FC<RouteComponentProps> = () => {
<Flex justifyContent="space-between" mt="1em" flexWrap="wrap">
<Button
onClick={() => {
sketch.clear()
setBg({ ...bg })
sketch.clear();
setBg({ ...bg });
}}
icon={<FaBomb />}
outlined
@ -342,7 +346,7 @@ const MapPlannerPage: React.FC<RouteComponentProps> = () => {
<MapSelect bg={bg} setBg={setBg} />
</Flex>
</>
)
}
);
};
export default MapPlannerPage
export default MapPlannerPage;

View File

@ -1,14 +1,14 @@
import { Flex, FormLabel, Switch } from "@chakra-ui/core"
import React, { useContext } from "react"
import MyThemeContext from "../../themeContext"
import Select from "../elements/Select"
import ModeButtons from "../xtrends/ModeButtons"
import { PlannerMapBg } from "./MapPlannerPage"
import { useTranslation } from "react-i18next"
import { Flex, FormLabel, Switch } from "@chakra-ui/core";
import React, { useContext } from "react";
import MyThemeContext from "../../themeContext";
import Select from "../elements/Select";
import ModeButtons from "../xtrends/ModeButtons";
import { PlannerMapBg } from "./MapPlannerPage";
import { useTranslation } from "react-i18next";
interface MapSelectProps {
bg: PlannerMapBg
setBg: React.Dispatch<React.SetStateAction<PlannerMapBg>>
bg: PlannerMapBg;
setBg: React.Dispatch<React.SetStateAction<PlannerMapBg>>;
}
const maps = [
@ -68,11 +68,11 @@ const maps = [
label: "Skipper Pavilion",
value: "Skipper Pavilion",
},
]
];
const MapSelect: React.FC<MapSelectProps> = ({ bg, setBg }) => {
const { themeColor } = useContext(MyThemeContext)
const { t } = useTranslation()
const { themeColor } = useContext(MyThemeContext);
const { t } = useTranslation();
return (
<Flex
@ -110,7 +110,7 @@ const MapSelect: React.FC<MapSelectProps> = ({ bg, setBg }) => {
/>
</Flex>
</Flex>
)
}
);
};
export default MapSelect
export default MapSelect;

View File

@ -1,17 +1,17 @@
import { Box, Flex, Grid } from "@chakra-ui/core"
import React, { useContext, useState } from "react"
import MyThemeContext from "../../themeContext"
import { months } from "../../utils/lists"
import UserAvatar from "../common/UserAvatar"
import Button from "../elements/Button"
import Markdown from "../elements/Markdown"
import { Box, Flex, Grid } from "@chakra-ui/core";
import React, { useContext, useState } from "react";
import MyThemeContext from "../../themeContext";
import { months } from "../../utils/lists";
import UserAvatar from "../common/UserAvatar";
import Button from "../elements/Button";
import Markdown from "../elements/Markdown";
interface VotingButtonProps {
value: 2 | 1 | -1 | -2
handleClick: (oldValue: number) => void
gridArea: string
active: boolean
lastTime: boolean
value: 2 | 1 | -1 | -2;
handleClick: (oldValue: number) => void;
gridArea: string;
active: boolean;
lastTime: boolean;
}
const buttonBg = {
@ -19,7 +19,7 @@ const buttonBg = {
"-1": "red.500",
"1": "green.500",
"2": "green.500",
} as const
} as const;
const VotingButton: React.FC<VotingButtonProps> = ({
value,
@ -28,13 +28,13 @@ const VotingButton: React.FC<VotingButtonProps> = ({
active,
lastTime,
}) => {
const { grayWithShade } = useContext(MyThemeContext)
const { grayWithShade } = useContext(MyThemeContext);
const d = new Date()
let month = d.getMonth()
if (month === 0) month = 12
const d = new Date();
let month = d.getMonth();
if (month === 0) month = 12;
const monthStr = months[month]
const monthStr = months[month];
return (
<Flex
flexDirection="column"
@ -65,26 +65,26 @@ const VotingButton: React.FC<VotingButtonProps> = ({
</Box>
)}
</Flex>
)
}
);
};
interface PersonForVotingProps {
votes: Record<string, number>
setVotes: React.Dispatch<React.SetStateAction<Record<string, number>>>
votes: Record<string, number>;
setVotes: React.Dispatch<React.SetStateAction<Record<string, number>>>;
user: {
username: string
discriminator: string
avatar?: string
discord_id: string
bio?: string
}
username: string;
discriminator: string;
avatar?: string;
discord_id: string;
bio?: string;
};
suggester?: {
username: string
discriminator: string
}
description?: string
sameRegion?: boolean
oldVote?: number
username: string;
discriminator: string;
};
description?: string;
sameRegion?: boolean;
oldVote?: number;
}
const PersonForVoting: React.FC<PersonForVotingProps> = ({
@ -96,12 +96,12 @@ const PersonForVoting: React.FC<PersonForVotingProps> = ({
oldVote,
sameRegion = true,
}) => {
const [showBio, setShowBio] = useState(false)
const { grayWithShade } = useContext(MyThemeContext)
const [showBio, setShowBio] = useState(false);
const { grayWithShade } = useContext(MyThemeContext);
const handleClick = (value: number) => {
setVotes({ ...votes, [user.discord_id]: value })
}
setVotes({ ...votes, [user.discord_id]: value });
};
return (
<Box
@ -199,7 +199,7 @@ const PersonForVoting: React.FC<PersonForVotingProps> = ({
</Box>
)}
</Box>
)
}
);
};
export default PersonForVoting
export default PersonForVoting;

View File

@ -6,15 +6,15 @@ import {
AccordionPanel,
Box,
Link,
} from "@chakra-ui/core"
import { RouteComponentProps } from "@reach/router"
import React, { useContext } from "react"
import { Helmet } from "react-helmet-async"
import MyThemeContext from "../../themeContext"
import PageHeader from "../common/PageHeader"
} from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import React, { useContext } from "react";
import { Helmet } from "react-helmet-async";
import MyThemeContext from "../../themeContext";
import PageHeader from "../common/PageHeader";
export const PlusFAQPage: React.FC<RouteComponentProps> = () => {
const { themeColorWithShade, darkerBgColor } = useContext(MyThemeContext)
const { themeColorWithShade, darkerBgColor } = useContext(MyThemeContext);
const questionsAndAnswers = [
{
@ -119,7 +119,7 @@ export const PlusFAQPage: React.FC<RouteComponentProps> = () => {
</>
),
},
]
];
return (
<>
@ -143,7 +143,7 @@ export const PlusFAQPage: React.FC<RouteComponentProps> = () => {
))}
</Accordion>
</>
)
}
);
};
export default PlusFAQPage
export default PlusFAQPage;

View File

@ -1,7 +1,7 @@
import { RouteComponentProps } from "@reach/router"
import React from "react"
import { Helmet } from "react-helmet-async"
import Suggestions from "./Suggestions"
import { RouteComponentProps } from "@reach/router";
import React from "react";
import { Helmet } from "react-helmet-async";
import Suggestions from "./Suggestions";
const PlusPage: React.FC<RouteComponentProps> = () => {
return (
@ -11,7 +11,7 @@ const PlusPage: React.FC<RouteComponentProps> = () => {
</Helmet>
<Suggestions />
</>
)
}
);
};
export default PlusPage
export default PlusPage;

View File

@ -1,46 +1,46 @@
import React, { useState, useContext } from "react"
import Modal from "../elements/Modal"
import UserSelector from "../common/UserSelector"
import { ADD_SUGGESTION } from "../../graphql/mutations/addSuggestion"
import { ADD_VOUCH } from "../../graphql/mutations/addVouch"
import { useMutation } from "@apollo/react-hooks"
import { useMutation } from "@apollo/client";
import {
useToast,
FormControl,
FormLabel,
FormHelperText,
RadioGroup,
Radio,
Flex,
Box,
Flex,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Radio,
RadioGroup,
Stack,
} from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import TextArea from "../elements/TextArea"
import Button from "../elements/Button"
import { SUGGESTIONS } from "../../graphql/queries/suggestions"
import { VOUCHES } from "../../graphql/queries/vouches"
import { USER } from "../../graphql/queries/user"
useToast,
} from "@chakra-ui/core";
import React, { useContext, useState } from "react";
import { ADD_SUGGESTION } from "../../graphql/mutations/addSuggestion";
import { ADD_VOUCH } from "../../graphql/mutations/addVouch";
import { SUGGESTIONS } from "../../graphql/queries/suggestions";
import { USER } from "../../graphql/queries/user";
import { VOUCHES } from "../../graphql/queries/vouches";
import MyThemeContext from "../../themeContext";
import UserSelector from "../common/UserSelector";
import Button from "../elements/Button";
import Modal from "../elements/Modal";
import TextArea from "../elements/TextArea";
interface AddSuggestionVars {
discord_id: string
region: string
server: string
description: string
discord_id: string;
region: string;
server: string;
description: string;
}
interface AddVouchVars {
discord_id: string
region: string
server: string
discord_id: string;
region: string;
server: string;
}
interface SuggestionVouchModalProps {
closeModal: () => void
canSuggest: boolean
canVouch: boolean
plusServer: "ONE" | "TWO"
closeModal: () => void;
canSuggest: boolean;
canVouch: boolean;
plusServer: "ONE" | "TWO";
}
const SuggestionVouchModal: React.FC<SuggestionVouchModalProps> = ({
@ -49,26 +49,26 @@ const SuggestionVouchModal: React.FC<SuggestionVouchModalProps> = ({
canVouch,
plusServer,
}) => {
const [form, setForm] = useState<Partial<AddSuggestionVars>>({})
const [form, setForm] = useState<Partial<AddSuggestionVars>>({});
const [actionType, setActionType] = useState<string>(
!canSuggest ? "VOUCH" : "SUGGEST"
)
const [showErrors, setShowErrors] = useState(false)
const toast = useToast()
const { themeColor } = useContext(MyThemeContext)
);
const [showErrors, setShowErrors] = useState(false);
const toast = useToast();
const { themeColor } = useContext(MyThemeContext);
const [addSuggestion, { loading }] = useMutation<boolean, AddSuggestionVars>(
ADD_SUGGESTION,
{
variables: { ...(form as AddSuggestionVars) },
onCompleted: (data) => {
closeModal()
closeModal();
toast({
description: `Suggestion added`,
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -77,11 +77,11 @@ const SuggestionVouchModal: React.FC<SuggestionVouchModalProps> = ({
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [{ query: SUGGESTIONS }],
}
)
);
const [addVouch, { loading: vouchLoading }] = useMutation<
boolean,
@ -89,13 +89,13 @@ const SuggestionVouchModal: React.FC<SuggestionVouchModalProps> = ({
>(ADD_VOUCH, {
variables: { ...(form as AddVouchVars) },
onCompleted: (data) => {
closeModal()
closeModal();
toast({
description: `Player vouched`,
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -104,32 +104,32 @@ const SuggestionVouchModal: React.FC<SuggestionVouchModalProps> = ({
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [{ query: VOUCHES }, { query: USER }],
})
});
const handleChange = (newValueObject: Partial<AddSuggestionVars>) => {
setForm({ ...form, ...newValueObject })
}
setForm({ ...form, ...newValueObject });
};
const handleSubmit = () => {
if (!form.discord_id || !form.region || !form.server) {
setShowErrors(true)
return
setShowErrors(true);
return;
}
if (
(!form.description ||
(form.description && form.description.length > 1000)) &&
actionType === "SUGGEST"
) {
setShowErrors(true)
return
setShowErrors(true);
return;
}
if (actionType === "SUGGEST") addSuggestion()
if (actionType === "VOUCH") addVouch()
}
if (actionType === "SUGGEST") addSuggestion();
if (actionType === "VOUCH") addVouch();
};
return (
<Modal title="Suggesting or vouching a player" closeModal={closeModal}>
@ -261,7 +261,7 @@ const SuggestionVouchModal: React.FC<SuggestionVouchModalProps> = ({
</Button>
</Flex>
</Modal>
)
}
);
};
export default SuggestionVouchModal
export default SuggestionVouchModal;

View File

@ -1,79 +1,79 @@
import { useQuery } from "@apollo/react-hooks"
import { Badge, Box, Divider, Flex, Grid } from "@chakra-ui/core"
import { Link } from "@reach/router"
import React, { useContext, useState } from "react"
import { PlusInfoData, PLUS_INFO } from "../../graphql/queries/plusInfo"
import { useQuery } from "@apollo/client";
import { Badge, Box, Divider, Flex, Grid } from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext, useState } from "react";
import { PlusInfoData, PLUS_INFO } from "../../graphql/queries/plusInfo";
import {
Suggestion,
SUGGESTIONS,
SuggestionsData,
} from "../../graphql/queries/suggestions"
import { USER } from "../../graphql/queries/user"
import { VOUCHES } from "../../graphql/queries/vouches"
import MyThemeContext from "../../themeContext"
import { UserData } from "../../types"
import Error from "../common/Error"
import Loading from "../common/Loading"
import SubHeader from "../common/SubHeader"
import UserAvatar from "../common/UserAvatar"
import Button from "../elements/Button"
import SuggestionVouchModal from "./SuggestionVouchModal"
} from "../../graphql/queries/suggestions";
import { USER } from "../../graphql/queries/user";
import { VOUCHES } from "../../graphql/queries/vouches";
import MyThemeContext from "../../themeContext";
import { UserData } from "../../types";
import Error from "../common/Error";
import Loading from "../common/Loading";
import SubHeader from "../common/SubHeader";
import UserAvatar from "../common/UserAvatar";
import Button from "../elements/Button";
import SuggestionVouchModal from "./SuggestionVouchModal";
interface VouchUser {
username: string
discriminator: string
avatar?: string
discord_id: string
username: string;
discriminator: string;
avatar?: string;
discord_id: string;
plus: {
voucher_user: {
username: string
discriminator: string
discord_id: string
}
vouch_status: string
}
username: string;
discriminator: string;
discord_id: string;
};
vouch_status: string;
};
}
interface VouchesData {
vouches: VouchUser[]
vouches: VouchUser[];
}
const Suggestions = () => {
const { grayWithShade, themeColor } = useContext(MyThemeContext)
const { grayWithShade, themeColor } = useContext(MyThemeContext);
const { data, error, loading } = useQuery<SuggestionsData>(SUGGESTIONS)
const { data, error, loading } = useQuery<SuggestionsData>(SUGGESTIONS);
const {
data: vouchesData,
error: vouchesError,
loading: vouchesLoading,
} = useQuery<VouchesData>(VOUCHES)
const { data: userData, error: userError } = useQuery<UserData>(USER)
} = useQuery<VouchesData>(VOUCHES);
const { data: userData, error: userError } = useQuery<UserData>(USER);
const { data: plusInfoData, error: plusInfoError } = useQuery<PlusInfoData>(
PLUS_INFO
)
);
const [showSuggestionForm, setShowSuggestionForm] = useState(false)
const [showSuggestionForm, setShowSuggestionForm] = useState(false);
if (error) return <Error errorMessage={error.message} />
if (userError) return <Error errorMessage={userError.message} />
if (plusInfoError) return <Error errorMessage={plusInfoError.message} />
if (vouchesError) return <Error errorMessage={vouchesError.message} />
if (loading || vouchesLoading || !data || !vouchesData) return <Loading />
if (!data.suggestions) return null
if (error) return <Error errorMessage={error.message} />;
if (userError) return <Error errorMessage={userError.message} />;
if (plusInfoError) return <Error errorMessage={plusInfoError.message} />;
if (vouchesError) return <Error errorMessage={vouchesError.message} />;
if (loading || vouchesLoading || !data || !vouchesData) return <Loading />;
if (!data.suggestions) return null;
const plusOneVouches = vouchesData.vouches.filter(
(vouch) => vouch.plus.vouch_status === "ONE"
)
);
const plusTwoVouches = vouchesData.vouches.filter(
(vouch) => vouch.plus.vouch_status === "TWO"
)
);
const plusOneSuggested = data.suggestions.filter(
(suggestion) => suggestion.plus_server === "ONE"
)
);
const plusTwoSuggested = data.suggestions.filter(
(suggestion) => suggestion.plus_server === "TWO"
)
);
const vouchMap = (vouch: VouchUser) => {
return (
@ -93,12 +93,12 @@ const Suggestions = () => {
</Box>
</Box>
</React.Fragment>
)
}
);
};
const suggestionMap = (suggestion: Suggestion, index: number) => {
const suggested_user = suggestion.discord_user
const suggester_user = suggestion.suggester_discord_user
const suggested_user = suggestion.discord_user;
const suggester_user = suggestion.suggester_discord_user;
return (
<React.Fragment key={suggestion.discord_user.discord_id}>
{index !== 0 && <Divider my="1rem" />}
@ -122,19 +122,20 @@ const Suggestions = () => {
{suggestion.description}
</Box>
</React.Fragment>
)
}
);
};
const user = userData?.user
const user = userData?.user;
const ownSuggestion = data.suggestions.find(
(suggestion) =>
suggestion.suggester_discord_user.discord_id === user?.discord_id
)
);
const canSuggest = !ownSuggestion
const canSuggest = !ownSuggestion;
const canVouch = !!user?.plus?.can_vouch && !user?.plus?.can_vouch_again_after
const canVouch =
!!user?.plus?.can_vouch && !user?.plus?.can_vouch_again_after;
const getButtonText = () => {
// can't suggest or vouch if voting is underway OR is not plus server member
@ -142,14 +143,14 @@ const Suggestions = () => {
plusInfoData?.plusInfo?.voting_ends ||
!userData?.user?.plus?.membership_status
)
return undefined
return undefined;
if (canSuggest && canVouch) return "Suggest or vouch a player"
else if (canSuggest) return "Suggest a player"
else if (canVouch) return "Vouch a player"
}
if (canSuggest && canVouch) return "Suggest or vouch a player";
else if (canSuggest) return "Suggest a player";
else if (canVouch) return "Vouch a player";
};
const buttonText = getButtonText()
const buttonText = getButtonText();
return (
<>
@ -206,7 +207,7 @@ const Suggestions = () => {
{plusTwoSuggested.map(suggestionMap)}
</Box>
</>
)
}
);
};
export default Suggestions
export default Suggestions;

View File

@ -1,6 +1,6 @@
import React, { useContext } from "react"
import UserAvatar from "../common/UserAvatar"
import { Summary } from "./VotingHistoryPage"
import React, { useContext } from "react";
import UserAvatar from "../common/UserAvatar";
import { Summary } from "./VotingHistoryPage";
import {
Heading,
Box,
@ -12,22 +12,22 @@ import {
PopoverContent,
PopoverArrow,
PopoverBody,
} from "@chakra-ui/core"
import { Link } from "@reach/router"
import MyThemeContext from "../../themeContext"
import { FaBolt } from "react-icons/fa"
} from "@chakra-ui/core";
import { Link } from "@reach/router";
import MyThemeContext from "../../themeContext";
import { FaBolt } from "react-icons/fa";
const getColor = (score: number) =>
score < 50 ? { color: "red" } : { color: "green" }
score < 50 ? { color: "red" } : { color: "green" };
interface CountProps {
count: number[]
count: number[];
}
const Count: React.FC<CountProps> = ({ count }) => {
const { colorMode } = useContext(MyThemeContext)
const printOuter = count[0] !== 0 || count[3] !== 0
const shade = colorMode === "light" ? "600" : "400"
const { colorMode } = useContext(MyThemeContext);
const printOuter = count[0] !== 0 || count[3] !== 0;
const shade = colorMode === "light" ? "600" : "400";
return (
<Box as="span" fontSize="18px">
{printOuter && (
@ -54,27 +54,27 @@ const Count: React.FC<CountProps> = ({ count }) => {
</>
)}
</Box>
)
}
);
};
interface SummariesProps {
summaries: Summary[]
summaries: Summary[];
}
const Summaries: React.FC<SummariesProps> = ({ summaries }) => {
const { grayWithShade, themeColor, darkerBgColor } = useContext(
MyThemeContext
)
const members: Summary[] = []
const suggested: Summary[] = []
);
const members: Summary[] = [];
const suggested: Summary[] = [];
summaries.forEach((summary) => {
if (summary.suggested) suggested.push(summary)
else members.push(summary)
})
if (summary.suggested) suggested.push(summary);
else members.push(summary);
});
const summaryMap = (summary: Summary) => {
const { discord_user, score } = summary
const { discord_user, score } = summary;
return (
<React.Fragment key={discord_user.discord_id}>
<Flex mr="1em" alignItems="center">
@ -129,8 +129,8 @@ const Summaries: React.FC<SummariesProps> = ({ summaries }) => {
</Box>
</Box>
</React.Fragment>
)
}
);
};
return (
<>
@ -144,7 +144,7 @@ const Summaries: React.FC<SummariesProps> = ({ summaries }) => {
{suggested.map(summaryMap)}
</Grid>
</>
)
}
);
};
export default Summaries
export default Summaries;

View File

@ -1,82 +1,82 @@
import { useQuery } from "@apollo/react-hooks"
import { Box, Flex } from "@chakra-ui/core"
import { RouteComponentProps } from "@reach/router"
import React, { useEffect, useState } from "react"
import { Helmet } from "react-helmet-async"
import { SUMMARIES } from "../../graphql/queries/summaries"
import { months } from "../../utils/lists"
import Error from "../common/Error"
import Loading from "../common/Loading"
import PageHeader from "../common/PageHeader"
import Select from "../elements/Select"
import Summaries from "./Summaries"
import { useQuery } from "@apollo/client";
import { Box, Flex } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import React, { useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import { SUMMARIES } from "../../graphql/queries/summaries";
import { months } from "../../utils/lists";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import Select from "../elements/Select";
import Summaries from "./Summaries";
export interface Summary {
discord_user: {
discord_id: string
username: string
discriminator: string
avatar?: string
}
discord_id: string;
username: string;
discriminator: string;
avatar?: string;
};
score: {
total: number
eu_count?: number[]
na_count?: number[]
}
plus_server: "ONE" | "TWO"
suggested: boolean
vouched: boolean
year: number
month: number
total: number;
eu_count?: number[];
na_count?: number[];
};
plus_server: "ONE" | "TWO";
suggested: boolean;
vouched: boolean;
year: number;
month: number;
}
interface SummariesData {
summaries: Summary[]
summaries: Summary[];
}
const VotingHistoryPage: React.FC<RouteComponentProps> = () => {
const { data, loading, error } = useQuery<SummariesData>(SUMMARIES)
const [monthChoices, setMonthChoices] = useState<string[]>([])
const { data, loading, error } = useQuery<SummariesData>(SUMMARIES);
const [monthChoices, setMonthChoices] = useState<string[]>([]);
const [forms, setForms] = useState<
Partial<{ plus_server: "+1" | "+2"; monthYear: string }>
>({})
>({});
useEffect(() => {
if (!data) return
if (!data) return;
const monthsYears: string[] = data.summaries.reduce(
(
acc: {
contains: { [key: number]: { [key: number]: boolean } }
monthChoices: string[]
contains: { [key: number]: { [key: number]: boolean } };
monthChoices: string[];
},
cur: Summary
) => {
const { month, year } = cur
if (!acc.contains[year]) acc.contains[year] = {}
const { month, year } = cur;
if (!acc.contains[year]) acc.contains[year] = {};
if (!acc.contains[year][month]) {
acc.contains[year][month] = true
const monthString = `${months[month]} ${year}`
acc.monthChoices.push(monthString)
acc.contains[year][month] = true;
const monthString = `${months[month]} ${year}`;
acc.monthChoices.push(monthString);
}
return acc
return acc;
},
{ contains: {}, monthChoices: [] }
).monthChoices
).monthChoices;
setForms({
plus_server: "+1",
monthYear: monthsYears[0],
})
setMonthChoices(monthsYears)
}, [data])
});
setMonthChoices(monthsYears);
}, [data]);
if (error) return <Error errorMessage={error.message} />
if (loading || !forms.monthYear) return <Loading />
if (error) return <Error errorMessage={error.message} />;
if (loading || !forms.monthYear) return <Loading />;
const parts = forms.monthYear.split(" ")
const month = months.indexOf(parts[0] as any)
const year = parseInt(parts[1])
const parts = forms.monthYear.split(" ");
const month = months.indexOf(parts[0] as any);
const year = parseInt(parts[1]);
return (
<>
<Helmet>
@ -106,16 +106,16 @@ const VotingHistoryPage: React.FC<RouteComponentProps> = () => {
</Flex>
<Summaries
summaries={data!.summaries.filter((summary) => {
const server = summary.plus_server === "ONE" ? "+1" : "+2"
const server = summary.plus_server === "ONE" ? "+1" : "+2";
return (
summary.month === month &&
summary.year === year &&
server === forms.plus_server
)
);
})}
/>
</>
)
}
);
};
export default VotingHistoryPage
export default VotingHistoryPage;

View File

@ -1,60 +1,58 @@
import { useMutation, useQuery } from "@apollo/react-hooks"
import { Box, Flex, Progress, useToast } from "@chakra-ui/core"
import { Redirect, RouteComponentProps } from "@reach/router"
import React, { useContext, useEffect, useState } from "react"
import { AddVotesVars, ADD_VOTES } from "../../graphql/mutations/addVotes"
import { PlusInfoData, PLUS_INFO } from "../../graphql/queries/plusInfo"
import { USER } from "../../graphql/queries/user"
import { useMutation, useQuery } from "@apollo/client";
import { Box, Flex, Progress, useToast } from "@chakra-ui/core";
import { Redirect, RouteComponentProps } from "@reach/router";
import React, { useContext, useEffect, useState } from "react";
import { AddVotesVars, ADD_VOTES } from "../../graphql/mutations/addVotes";
import { PlusInfoData, PLUS_INFO } from "../../graphql/queries/plusInfo";
import { USER } from "../../graphql/queries/user";
import {
UsersForVotingData,
USERS_FOR_VOTING,
VotingSuggested,
} from "../../graphql/queries/usersForVoting"
import MyThemeContext from "../../themeContext"
import { UserData } from "../../types"
import Error from "../common/Error"
import Loading from "../common/Loading"
import PageHeader from "../common/PageHeader"
import SubHeader from "../common/SubHeader"
import Alert from "../elements/Alert"
import Button from "../elements/Button"
import PersonForVoting from "./PersonForVoting"
} from "../../graphql/queries/usersForVoting";
import MyThemeContext from "../../themeContext";
import { UserData } from "../../types";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import SubHeader from "../common/SubHeader";
import Alert from "../elements/Alert";
import Button from "../elements/Button";
import PersonForVoting from "./PersonForVoting";
interface SuggestedArrays {
sameRegion: VotingSuggested[]
otherRegion: VotingSuggested[]
sameRegion: VotingSuggested[];
otherRegion: VotingSuggested[];
}
const VotingPage: React.FC<RouteComponentProps> = () => {
const { themeColor, grayWithShade, colorMode } = useContext(MyThemeContext)
const { themeColor, grayWithShade, colorMode } = useContext(MyThemeContext);
const { data, loading, error } = useQuery<UsersForVotingData>(
USERS_FOR_VOTING
)
const { data: plusInfoData, loading: plusInfoLoading } = useQuery<
PlusInfoData
>(PLUS_INFO)
const { data: userData } = useQuery<UserData>(USER)
);
const { data: plusInfoData } = useQuery<PlusInfoData>(PLUS_INFO);
const { data: userData } = useQuery<UserData>(USER);
const [votes, setVotes] = useState<Record<string, number>>({})
const [oldVotes, setOldVotes] = useState<Record<string, number>>({})
const [votes, setVotes] = useState<Record<string, number>>({});
const [oldVotes, setOldVotes] = useState<Record<string, number>>({});
const [suggestedArrays, setSuggestedArrays] = useState<SuggestedArrays>({
sameRegion: [],
otherRegion: [],
})
const toast = useToast()
});
const toast = useToast();
const [addVotesMutation, { loading: addVotesLoading }] = useMutation<
boolean,
AddVotesVars
>(ADD_VOTES, {
onCompleted: () => {
window.scrollTo(0, 0)
window.scrollTo(0, 0);
toast({
description: `Votes submitted`,
position: "top-right",
status: "success",
duration: 10000,
})
});
},
onError: (error) => {
toast({
@ -63,7 +61,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
position: "top-right",
status: "error",
duration: 10000,
})
});
},
refetchQueries: [
{
@ -73,7 +71,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
query: PLUS_INFO,
},
],
})
});
const handleSubmit = async () => {
await addVotesMutation({
@ -83,45 +81,45 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
score: (votes as any)[key],
})),
},
})
}
});
};
const user = userData?.user
const user = userData?.user;
useEffect(() => {
if (loading || error || !user) return
if (loading || error || !user) return;
const sameRegionSuggested: VotingSuggested[] = []
const otherRegionSuggested: VotingSuggested[] = []
const sameRegionSuggested: VotingSuggested[] = [];
const otherRegionSuggested: VotingSuggested[] = [];
data!.usersForVoting.suggested.forEach((suggested) => {
if (suggested.plus_region === user.plus!.plus_region) {
sameRegionSuggested.push(suggested)
sameRegionSuggested.push(suggested);
} else {
otherRegionSuggested.push(suggested)
otherRegionSuggested.push(suggested);
}
})
});
setSuggestedArrays({
sameRegion: sameRegionSuggested,
otherRegion: otherRegionSuggested,
})
});
if (data!.usersForVoting.votes) {
const voteObj: Record<string, number> = {}
const oldVoteObj: Record<string, number> = {}
const voteObj: Record<string, number> = {};
const oldVoteObj: Record<string, number> = {};
data!.usersForVoting.votes.forEach((vote) =>
vote.stale
? (oldVoteObj[vote.discord_id] = vote.score)
: (voteObj[vote.discord_id] = vote.score)
)
setVotes(voteObj)
setOldVotes(oldVoteObj)
);
setVotes(voteObj);
setOldVotes(oldVoteObj);
}
}, [loading, error, data, user])
}, [loading, error, data, user]);
if (plusInfoData && !plusInfoData.plusInfo) {
return <Redirect to="/404" />
return <Redirect to="/access" />;
}
if (
loading ||
@ -129,30 +127,30 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
!plusInfoData?.plusInfo ||
!plusInfoData.plusInfo.voting_ends
)
return <Loading />
if (error) return <Error errorMessage={error.message} />
return <Loading />;
if (error) return <Error errorMessage={error.message} />;
const votingEnds = parseInt(plusInfoData.plusInfo.voting_ends)
const votingEnds = parseInt(plusInfoData.plusInfo.voting_ends);
const {
voter_count: votedSoFar,
eligible_voters: eligibleVoters,
} = plusInfoData.plusInfo
} = plusInfoData.plusInfo;
const date = new Date()
const date = new Date();
if (votingEnds < date.getTime())
return (
<Alert status="info">
Voting is over. Results will be posted a bit later.
</Alert>
)
);
const hoursLeft = Math.ceil((votingEnds - date.getTime()) / (1000 * 60 * 60))
const alreadyVoted = votes.length > 0
const hoursLeft = Math.ceil((votingEnds - date.getTime()) / (1000 * 60 * 60));
const alreadyVoted = votes.length > 0;
const missingVotes =
data!.usersForVoting.users.length +
data!.usersForVoting.suggested.length -
Object.keys(votes).length
Object.keys(votes).length;
return (
<>
@ -177,7 +175,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
</SubHeader>
{data!.usersForVoting.users.map((userForVoting) => {
if (userForVoting.plus.plus_region !== user.plus!.plus_region)
return null
return null;
return (
<PersonForVoting
key={userForVoting.discord_id}
@ -186,7 +184,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
setVotes={setVotes}
oldVote={oldVotes[userForVoting.discord_id]}
/>
)
);
})}
</Box>
{suggestedArrays.sameRegion.length > 0 && (
@ -206,7 +204,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
description={suggestion.description}
oldVote={oldVotes[suggestion.discord_user.discord_id]}
/>
)
);
})}
</>
)}
@ -215,7 +213,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
</SubHeader>
{data!.usersForVoting.users.map((userForVoting) => {
if (userForVoting.plus.plus_region === user.plus!.plus_region)
return null
return null;
return (
<PersonForVoting
key={userForVoting.discord_id}
@ -225,7 +223,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
sameRegion={false}
oldVote={oldVotes[userForVoting.discord_id]}
/>
)
);
})}
{suggestedArrays.otherRegion.length > 0 && (
<>
@ -246,7 +244,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
description={suggestion.description}
oldVote={oldVotes[suggestion.discord_user.discord_id]}
/>
)
);
})}
</>
)}
@ -268,7 +266,7 @@ const VotingPage: React.FC<RouteComponentProps> = () => {
)}
</Flex>
</>
)
}
);
};
export default VotingPage
export default VotingPage;

View File

@ -1,64 +1,64 @@
import { useQuery } from "@apollo/react-hooks"
import { Avatar, Box, Flex, Grid } from "@chakra-ui/core"
import { Link, RouteComponentProps } from "@reach/router"
import React, { useContext, useEffect, useState } from "react"
import { Helmet } from "react-helmet-async"
import { useTranslation } from "react-i18next"
import { FaExternalLinkAlt, FaLongArrowAltLeft } from "react-icons/fa"
import { modeIconMap } from "../../assets/icons"
import { mapIcons } from "../../assets/imageImports"
import { useQuery } from "@apollo/client";
import { Avatar, Box, Flex, Grid } from "@chakra-ui/core";
import { Link, RouteComponentProps } from "@reach/router";
import React, { useContext, useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import { FaExternalLinkAlt, FaLongArrowAltLeft } from "react-icons/fa";
import { modeIconMap } from "../../assets/icons";
import { mapIcons } from "../../assets/imageImports";
import {
SearchForDraftCupData,
SearchForDraftCupVars,
SEARCH_FOR_DRAFT_CUP,
} from "../../graphql/queries/searchForDraftCup"
import MyThemeContext from "../../themeContext"
} from "../../graphql/queries/searchForDraftCup";
import MyThemeContext from "../../themeContext";
import {
Ability,
ClothingGear,
DetailedTeamInfo,
HeadGear,
ShoesGear,
} from "../../types"
import AbilityIcon from "../builds/AbilityIcon"
import GearImage from "../builds/GearImage"
import Error from "../common/Error"
import Loading from "../common/Loading"
import Section from "../common/Section"
import SplatnetIcon from "../common/SplatnetIcon"
import WeaponImage from "../common/WeaponImage"
import Button from "../elements/Button"
import { DraftTournamentCard } from "./DraftTournamentCards"
} from "../../types";
import AbilityIcon from "../builds/AbilityIcon";
import GearImage from "../builds/GearImage";
import Error from "../common/Error";
import Loading from "../common/Loading";
import Section from "../common/Section";
import SplatnetIcon from "../common/SplatnetIcon";
import WeaponImage from "../common/WeaponImage";
import Button from "../elements/Button";
import { DraftTournamentCard } from "./DraftTournamentCards";
interface DraftCupDetailsProps {
id?: string
id?: string;
}
interface DetailedMapCardProps {
mapDetails: {
stage: string
mode: "TW" | "SZ" | "TC" | "RM" | "CB"
duration: number
winners: DetailedTeamInfo
losers: DetailedTeamInfo
}
gameNumber: number
stage: string;
mode: "TW" | "SZ" | "TC" | "RM" | "CB";
duration: number;
winners: DetailedTeamInfo;
losers: DetailedTeamInfo;
};
gameNumber: number;
}
interface CollapsedMapCardProps {
mapDetails: {
stage: string
mode: "TW" | "SZ" | "TC" | "RM" | "CB"
duration: number
winners: DetailedTeamInfo
losers: DetailedTeamInfo
}[]
expand: () => void
loading: boolean
stage: string;
mode: "TW" | "SZ" | "TC" | "RM" | "CB";
duration: number;
winners: DetailedTeamInfo;
losers: DetailedTeamInfo;
}[];
expand: () => void;
loading: boolean;
}
const abilityMap = (ability: Ability, index: number) => {
const gridArea = `5 / ${1 + index} / 6 / ${2 + index}`
const gridArea = `5 / ${1 + index} / 6 / ${2 + index}`;
return (
<Flex
gridArea={gridArea}
@ -68,31 +68,31 @@ const abilityMap = (ability: Ability, index: number) => {
>
<AbilityIcon ability={ability ? ability : "UNKNOWN"} size="TINY" />
</Flex>
)
}
);
};
const gearMap = (gear: HeadGear | ClothingGear | ShoesGear, index: number) => {
const gridArea = `4 / ${1 + index} / 5 / ${2 + index}`
const gridArea = `4 / ${1 + index} / 5 / ${2 + index}`;
return (
<Flex gridArea={gridArea} key={index} justifyContent="center">
<GearImage englishName={gear} mini />
</Flex>
)
}
);
};
const DetailedMapCard: React.FC<DetailedMapCardProps> = ({
mapDetails,
gameNumber,
}) => {
const { t } = useTranslation()
const { t } = useTranslation();
const { themeColorWithShade, grayWithShade, textColor } = useContext(
MyThemeContext
)
);
const minutes = Math.floor(mapDetails.duration / 60)
const seconds = mapDetails.duration - minutes * 60
const minutes = Math.floor(mapDetails.duration / 60);
const seconds = mapDetails.duration - minutes * 60;
const ModeIcon = modeIconMap[mapDetails.mode]
const ModeIcon = modeIconMap[mapDetails.mode];
return (
<Section
@ -232,37 +232,37 @@ const DetailedMapCard: React.FC<DetailedMapCardProps> = ({
))}
</Flex>
</Flex>
)
);
})}
</Section>
)
}
);
};
const CollapsedMapCard: React.FC<CollapsedMapCardProps> = ({
expand,
mapDetails,
loading,
}) => {
const { t } = useTranslation()
const { grayWithShade } = useContext(MyThemeContext)
const winnerTeamName = mapDetails[0].winners.team_name
const loserTeamName = mapDetails[0].losers.team_name
const winnerPlayers = mapDetails[0].winners.players
const loserPlayers = mapDetails[0].losers.players
const { t } = useTranslation();
const { grayWithShade } = useContext(MyThemeContext);
const winnerTeamName = mapDetails[0].winners.team_name;
const loserTeamName = mapDetails[0].losers.team_name;
const winnerPlayers = mapDetails[0].winners.players;
const loserPlayers = mapDetails[0].losers.players;
const teamData = {
[winnerTeamName]: { players: winnerPlayers, score: 0 },
[loserTeamName]: { players: loserPlayers, score: 0 },
}
};
mapDetails.forEach((stage) => {
teamData[stage.winners.team_name].score =
teamData[stage.winners.team_name].score + 1
})
teamData[stage.winners.team_name].score + 1;
});
const scores = Object.keys(teamData)
.map((key) => {
return { players: teamData[key].players, score: teamData[key].score }
return { players: teamData[key].players, score: teamData[key].score };
})
.sort((a, b) => b.score - a.score)
.sort((a, b) => b.score - a.score);
return (
<Section
@ -293,15 +293,15 @@ const CollapsedMapCard: React.FC<CollapsedMapCardProps> = ({
{t("draft;Expand")}
</Button>
</Section>
)
}
);
};
const DraftCupDetails: React.FC<RouteComponentProps & DraftCupDetailsProps> = ({
id,
}) => {
const { t } = useTranslation()
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext)
const idParts = id!.split("-")
const { t } = useTranslation();
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext);
const idParts = id!.split("-");
const { data, error, loading } = useQuery<
SearchForDraftCupData,
SearchForDraftCupVars
@ -311,19 +311,19 @@ const DraftCupDetails: React.FC<RouteComponentProps & DraftCupDetailsProps> = ({
idParts[1]
} ${idParts[2]}`,
},
})
const [expanded, setExpanded] = useState<number | null>(null)
const [loadingSet, setLoadingSet] = useState<number | null>(null)
});
const [expanded, setExpanded] = useState<number | null>(null);
const [loadingSet, setLoadingSet] = useState<number | null>(null);
useEffect(() => {
if (!loadingSet) return
setExpanded(loadingSet)
}, [loadingSet])
if (!loadingSet) return;
setExpanded(loadingSet);
}, [loadingSet]);
if (loading) return <Loading />
if (error) return <Error errorMessage={error.message} />
if (loading) return <Loading />;
if (error) return <Error errorMessage={error.message} />;
const { tournament, matches } = data!.searchForDraftCup
const { tournament, matches } = data!.searchForDraftCup;
return (
<>
@ -376,10 +376,10 @@ const DraftCupDetails: React.FC<RouteComponentProps & DraftCupDetailsProps> = ({
/>
)}
</Box>
)
);
})}
</>
)
}
);
};
export default DraftCupDetails
export default DraftCupDetails;

View File

@ -1,28 +1,28 @@
import { useQuery } from "@apollo/react-hooks"
import { Box } from "@chakra-ui/core"
import { RouteComponentProps } from "@reach/router"
import React, { useContext } from "react"
import { Helmet } from "react-helmet-async"
import { useTranslation } from "react-i18next"
import { useQuery } from "@apollo/client";
import { Box } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import React, { useContext } from "react";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import {
PlusDraftCupsData,
PLUS_DRAFT_CUPS,
} from "../../graphql/queries/plusDraftCups"
import MyThemeContext from "../../themeContext"
import Error from "../common/Error"
import Loading from "../common/Loading"
import PageHeader from "../common/PageHeader"
import Section from "../common/Section"
import DraftLeaderboard from "./DraftLeaderboard"
import DraftTournamentCards from "./DraftTournamentCards"
} from "../../graphql/queries/plusDraftCups";
import MyThemeContext from "../../themeContext";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import Section from "../common/Section";
import DraftLeaderboard from "./DraftLeaderboard";
import DraftTournamentCards from "./DraftTournamentCards";
const DraftCupPage: React.FC<RouteComponentProps> = () => {
const { t } = useTranslation()
const { grayWithShade } = useContext(MyThemeContext)
const { data, error, loading } = useQuery<PlusDraftCupsData>(PLUS_DRAFT_CUPS)
const { t } = useTranslation();
const { grayWithShade } = useContext(MyThemeContext);
const { data, error, loading } = useQuery<PlusDraftCupsData>(PLUS_DRAFT_CUPS);
if (error) return <Error errorMessage={error.message} />
if (loading) return <Loading />
if (error) return <Error errorMessage={error.message} />;
if (loading) return <Loading />;
return (
<>
@ -37,7 +37,7 @@ const DraftCupPage: React.FC<RouteComponentProps> = () => {
<DraftLeaderboard leaderboards={data!.plusDraftCups.leaderboards} />
</Section>
</>
)
}
);
};
export default DraftCupPage
export default DraftCupPage;

View File

@ -1,4 +1,4 @@
import React, { useContext } from "react"
import React, { useContext } from "react";
import {
Tabs,
TabList,
@ -9,50 +9,50 @@ import {
Box,
Flex,
Image,
} from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import UserAvatar from "../common/UserAvatar"
import { Link } from "@reach/router"
import { medalEmoji } from "../../assets/imageImports"
import useBreakPoints from "../../hooks/useBreakPoints"
} from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
import UserAvatar from "../common/UserAvatar";
import { Link } from "@reach/router";
import { medalEmoji } from "../../assets/imageImports";
import useBreakPoints from "../../hooks/useBreakPoints";
interface LeaderboardPlayer {
discord_user: {
username: string
discord_id: string
discriminator: string
avatar?: string
}
first: number
second: number
third: number
score: number
username: string;
discord_id: string;
discriminator: string;
avatar?: string;
};
first: number;
second: number;
third: number;
score: number;
}
interface DraftLeaderboardProps {
leaderboards: {
players: LeaderboardPlayer[]
type: "DRAFTONE" | "DRAFTTWO"
}[]
players: LeaderboardPlayer[];
type: "DRAFTONE" | "DRAFTTWO";
}[];
}
interface LeaderboardTabPanelProps {
players: LeaderboardPlayer[]
players: LeaderboardPlayer[];
}
const DraftLeaderboard: React.FC<DraftLeaderboardProps> = ({
leaderboards,
}) => {
const { themeColor, grayWithShade } = useContext(MyThemeContext)
const isSmall = useBreakPoints(550)
const { themeColor, grayWithShade } = useContext(MyThemeContext);
const isSmall = useBreakPoints(550);
const plusOneLeaderboard = leaderboards.find((lb) => lb.type === "DRAFTONE")
const plusTwoLeaderboard = leaderboards.find((lb) => lb.type === "DRAFTTWO")
const plusOneLeaderboard = leaderboards.find((lb) => lb.type === "DRAFTONE");
const plusTwoLeaderboard = leaderboards.find((lb) => lb.type === "DRAFTTWO");
if (!plusOneLeaderboard && !plusTwoLeaderboard) return null
if (!plusOneLeaderboard && !plusTwoLeaderboard) return null;
let placement = 1
let print = false
let placement = 1;
let print = false;
const LeaderboardTabPanel: React.FC<LeaderboardTabPanelProps> = ({
players,
}) => {
@ -65,13 +65,13 @@ const DraftLeaderboard: React.FC<DraftLeaderboardProps> = ({
maxW="550px"
>
{players.map((player, i, array) => {
print = false
print = false;
if (i === 0) {
placement = 1
print = true
placement = 1;
print = true;
} else if (i > 0 && player.score !== array[i - 1].score) {
placement = i + 1
print = true
placement = i + 1;
print = true;
}
return (
<React.Fragment key={player.discord_user.discord_id}>
@ -153,11 +153,11 @@ const DraftLeaderboard: React.FC<DraftLeaderboardProps> = ({
</>
)}
</React.Fragment>
)
);
})}
</Grid>
)
}
);
};
return (
<Tabs
@ -184,7 +184,7 @@ const DraftLeaderboard: React.FC<DraftLeaderboardProps> = ({
)}
</TabPanels>
</Tabs>
)
}
);
};
export default DraftLeaderboard
export default DraftLeaderboard;

View File

@ -1,64 +1,64 @@
import { Box, Flex, Grid, Image } from "@chakra-ui/core"
import { Link } from "@reach/router"
import React, { useContext } from "react"
import { useTranslation } from "react-i18next"
import { medalEmoji } from "../../assets/imageImports"
import trophy from "../../assets/trophy.png"
import MyThemeContext from "../../themeContext"
import { months } from "../../utils/lists"
import Section from "../common/Section"
import Button from "../elements/Button"
import { Box, Flex, Grid, Image } from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext } from "react";
import { useTranslation } from "react-i18next";
import { medalEmoji } from "../../assets/imageImports";
import trophy from "../../assets/trophy.png";
import MyThemeContext from "../../themeContext";
import { months } from "../../utils/lists";
import Section from "../common/Section";
import Button from "../elements/Button";
interface DraftTournamentCardsProps {
tournaments: {
name: string
top_3_team_names: string[]
name: string;
top_3_team_names: string[];
top_3_discord_users: {
username: string
discriminator: string
twitter_name?: string
discord_id: string
}[][]
bracket_url: string
date: string
type: "DRAFTONE" | "DRAFTTWO"
}[]
username: string;
discriminator: string;
twitter_name?: string;
discord_id: string;
}[][];
bracket_url: string;
date: string;
type: "DRAFTONE" | "DRAFTTWO";
}[];
}
interface DraftTournamentCardProps {
tournament: {
name: string
top_3_team_names: string[]
name: string;
top_3_team_names: string[];
top_3_discord_users: {
username: string
discriminator: string
twitter_name?: string
discord_id: string
}[][]
bracket_url: string
date: string
type: "DRAFTONE" | "DRAFTTWO"
}
link?: string
username: string;
discriminator: string;
twitter_name?: string;
discord_id: string;
}[][];
bracket_url: string;
date: string;
type: "DRAFTONE" | "DRAFTTWO";
};
link?: string;
}
interface MedalRowProps {
players: {
username: string
discriminator: string
twitter_name?: string
discord_id: string
}[]
medalImage: string
small?: boolean
username: string;
discriminator: string;
twitter_name?: string;
discord_id: string;
}[];
medalImage: string;
small?: boolean;
}
export const DraftTournamentCard: React.FC<DraftTournamentCardProps> = ({
tournament,
link,
}) => {
const { t, i18n } = useTranslation()
const { grayWithShade, themeColorWithShade } = useContext(MyThemeContext)
const { t, i18n } = useTranslation();
const { grayWithShade, themeColorWithShade } = useContext(MyThemeContext);
const MedalRow: React.FC<MedalRowProps> = ({
players,
@ -92,8 +92,8 @@ export const DraftTournamentCard: React.FC<DraftTournamentCardProps> = ({
</Box>
))}
</Flex>
)
}
);
};
return (
<Section
@ -146,8 +146,8 @@ export const DraftTournamentCard: React.FC<DraftTournamentCardProps> = ({
</Box>
)}
</Section>
)
}
);
};
const DraftTournamentCards: React.FC<DraftTournamentCardsProps> = ({
tournaments,
@ -160,7 +160,7 @@ const DraftTournamentCards: React.FC<DraftTournamentCardsProps> = ({
mt="1em"
>
{tournaments.map((tournament) => {
const date = new Date(parseInt(tournament.date))
const date = new Date(parseInt(tournament.date));
return (
<DraftTournamentCard
key={tournament.name}
@ -171,11 +171,11 @@ const DraftTournamentCards: React.FC<DraftTournamentCardsProps> = ({
date.getMonth() + 1
].toLowerCase()}-${date.getFullYear()}`}
/>
)
);
})}
</Grid>
</>
)
}
);
};
export default DraftTournamentCards
export default DraftTournamentCards;

View File

@ -1,13 +1,13 @@
import React, { useContext } from "react"
import { RouteComponentProps } from "@reach/router"
import { Heading, Link, List, ListItem } from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import { Helmet } from "react-helmet-async"
import { useTranslation, Trans } from "react-i18next"
import React, { useContext } from "react";
import { RouteComponentProps } from "@reach/router";
import { Heading, Link, List, ListItem } from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
import { Helmet } from "react-helmet-async";
import { useTranslation, Trans } from "react-i18next";
const About: React.FC<RouteComponentProps> = () => {
const { t } = useTranslation()
const { themeColorWithShade } = useContext(MyThemeContext)
const { t } = useTranslation();
const { themeColorWithShade } = useContext(MyThemeContext);
return (
<>
<Helmet>
@ -209,7 +209,7 @@ const About: React.FC<RouteComponentProps> = () => {
</ul>
</div>
</>
)
}
);
};
export default About
export default About;

View File

@ -1,9 +1,9 @@
import React from "react"
import { RouteComponentProps } from "@reach/router"
import Alert from "../elements/Alert"
import Button from "../elements/Button"
import { FaDiscord } from "react-icons/fa"
import { Box } from "@chakra-ui/core"
import { Box } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import React from "react";
import { DiscordIcon } from "../../assets/icons";
import Alert from "../elements/Alert";
import Button from "../elements/Button";
const Access: React.FC<RouteComponentProps> = () => {
return (
@ -13,11 +13,11 @@ const Access: React.FC<RouteComponentProps> = () => {
</Alert>
<Box mt="1em">
<a href="/auth/discord">
<Button icon={<FaDiscord />}>Log in via Discord</Button>
<Button icon={<DiscordIcon />}>Log in via Discord</Button>
</a>
</Box>
</>
)
}
);
};
export default Access
export default Access;

View File

@ -1,22 +1,24 @@
import { useColorMode, useTheme as useChakraTheme } from "@chakra-ui/core"
import useLocalStorage from "@rehooks/local-storage"
import React, { useEffect } from "react"
import { MyThemeProvider } from "../../themeContext"
import { Theme, ThemeColor } from "../../types"
import "./App.css"
import Layout from "./Layout"
import Routes from "./Routes"
import { useColorMode, useTheme as useChakraTheme } from "@chakra-ui/core";
import useLocalStorage from "@rehooks/local-storage";
import React, { useEffect } from "react";
import { MyThemeProvider } from "../../themeContext";
import { Theme, ThemeColor } from "../../types";
import "./App.css";
import Layout from "./Layout";
import Routes from "./Routes";
const App: React.FC = () => {
const chakraTheme = useChakraTheme()
const { colorMode } = useColorMode()
const [themeColorFromStorage] = useLocalStorage<ThemeColor>("colorPreference")
const chakraTheme = useChakraTheme();
const { colorMode } = useColorMode();
const [themeColorFromStorage] = useLocalStorage<ThemeColor>(
"colorPreference"
);
const themeColor = themeColorFromStorage
? themeColorFromStorage
: colorMode === "light"
? "pink"
: "orange"
: "orange";
const theme = {
light: {
@ -43,13 +45,13 @@ const App: React.FC = () => {
themeColorHexLighter: chakraTheme.colors[themeColor]["200"],
themeColor,
} as Theme,
}
};
useEffect(() => {
const favicon = document.getElementById("favicon") as HTMLLinkElement
if (!favicon) return
favicon.href = `/favicon_${themeColor}.png`
}, [themeColorFromStorage, themeColor])
const favicon = document.getElementById("favicon") as HTMLLinkElement;
if (!favicon) return;
favicon.href = `/favicon_${themeColor}.png`;
}, [themeColorFromStorage, themeColor]);
return (
<MyThemeProvider value={theme[colorMode]}>
@ -57,7 +59,7 @@ const App: React.FC = () => {
<Routes />
</Layout>
</MyThemeProvider>
)
}
);
};
export default App
export default App;

View File

@ -7,13 +7,13 @@ import {
MenuList,
MenuOptionGroup,
useTheme as useChakraTheme,
} from "@chakra-ui/core"
import { writeStorage } from "@rehooks/local-storage"
import React, { useContext } from "react"
import { ColorResult } from "react-color"
import { useTranslation } from "react-i18next"
import MyThemeContext from "../../themeContext"
import { themeColors as choices } from "../../utils/lists"
} from "@chakra-ui/core";
import { writeStorage } from "@rehooks/local-storage";
import React, { useContext } from "react";
import { ColorResult } from "react-color";
import { useTranslation } from "react-i18next";
import MyThemeContext from "../../themeContext";
import { themeColors as choices } from "../../utils/lists";
export const themeColors = [
"Gray",
@ -26,22 +26,24 @@ export const themeColors = [
"Cyan",
"Purple",
"Pink",
] as const
] as const;
const ColorSelector = () => {
const { t } = useTranslation()
const { themeColorHex, darkerBgColor, textColor } = useContext(MyThemeContext)
const theme = useChakraTheme()
const { t } = useTranslation();
const { themeColorHex, darkerBgColor, textColor } = useContext(
MyThemeContext
);
const theme = useChakraTheme();
const hexCodes = choices.map((color) =>
theme.colors[color]["500"].toLowerCase()
)
);
const handleColorChoice = (color: ColorResult) => {
const colorForLocalStorage = choices[hexCodes.indexOf(color)]
const colorForLocalStorage = choices[hexCodes.indexOf(color)];
writeStorage("colorPreference", colorForLocalStorage)
}
writeStorage("colorPreference", colorForLocalStorage);
};
return (
<Menu>
@ -83,7 +85,7 @@ const ColorSelector = () => {
</MenuOptionGroup>
</MenuList>
</Menu>
)
}
);
};
export default ColorSelector
export default ColorSelector;

View File

@ -1,15 +1,15 @@
import { Box, Image } from "@chakra-ui/core"
import React, { Suspense, useContext, useState } from "react"
import { footerOcto, footerSquid } from "../../assets/imageImports"
import MyThemeContext from "../../themeContext"
import FooterContent from "./FooterContent"
import { FooterWaves } from "./FooterWaves"
import { Box, Image } from "@chakra-ui/core";
import React, { Suspense, useContext, useState } from "react";
import { footerOcto, footerSquid } from "../../assets/imageImports";
import MyThemeContext from "../../themeContext";
import FooterContent from "./FooterContent";
import { FooterWaves } from "./FooterWaves";
const Footer: React.FC = () => {
const [footerBojoing] = useState(
Math.random() > 0.5 ? footerSquid : footerOcto
)
const { themeColorWithShade, colorMode } = useContext(MyThemeContext)
);
const { themeColorWithShade, colorMode } = useContext(MyThemeContext);
return (
<Box mt="auto">
@ -29,7 +29,7 @@ const Footer: React.FC = () => {
<FooterContent />
</Suspense>
</Box>
)
}
);
};
export default Footer
export default Footer;

View File

@ -1,14 +1,14 @@
import { Box, Flex } from "@chakra-ui/core"
import { Link } from "@reach/router"
import React, { useContext } from "react"
import { useTranslation } from "react-i18next"
import { FaGithub } from "react-icons/fa"
import { DiscordIcon } from "../../assets/icons"
import MyThemeContext from "../../themeContext"
import { Box, Flex } from "@chakra-ui/core";
import { Link } from "@reach/router";
import React, { useContext } from "react";
import { useTranslation } from "react-i18next";
import { FaGithub } from "react-icons/fa";
import { DiscordIcon } from "../../assets/icons";
import MyThemeContext from "../../themeContext";
const FooterContent: React.FC = () => {
const { t } = useTranslation()
const { themeColorWithShade } = useContext(MyThemeContext)
const { t } = useTranslation();
const { themeColorWithShade } = useContext(MyThemeContext);
return (
<Flex
pb="50px"
@ -36,7 +36,7 @@ const FooterContent: React.FC = () => {
</a>
</Flex>
</Flex>
)
}
);
};
export default FooterContent
export default FooterContent;

View File

@ -1,10 +1,10 @@
import React, { useContext } from "react"
import MyThemeContext from "../../themeContext"
import React, { useContext } from "react";
import MyThemeContext from "../../themeContext";
export const FooterWaves = () => {
const { themeColorHex, themeColorHexLighter, colorMode } = useContext(
MyThemeContext
)
);
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 225">
<path
@ -13,5 +13,5 @@ export const FooterWaves = () => {
d="M0,128L60,138.7C120,149,240,171,360,160C480,149,600,107,720,85.3C840,64,960,64,1080,90.7C1200,117,1320,171,1380,197.3L1440,224L1440,320L1380,320C1320,320,1200,320,1080,320C960,320,840,320,720,320C600,320,480,320,360,320C240,320,120,320,60,320L0,320Z"
></path>
</svg>
)
}
);
};

View File

@ -1,4 +1,4 @@
import { useQuery } from "@apollo/react-hooks"
import { useQuery } from "@apollo/client";
import {
Box,
Flex,
@ -8,43 +8,43 @@ import {
MenuGroup,
MenuItem,
MenuList,
} from "@chakra-ui/core"
import { Link, useLocation } from "@reach/router"
import React, { Suspense, useContext } from "react"
import { useTranslation } from "react-i18next"
import { PlusInfoData, PLUS_INFO } from "../../graphql/queries/plusInfo"
import MyThemeContext from "../../themeContext"
} from "@chakra-ui/core";
import { Link, useLocation } from "@reach/router";
import React, { Suspense, useContext } from "react";
import { useTranslation } from "react-i18next";
import { PlusInfoData, PLUS_INFO } from "../../graphql/queries/plusInfo";
import MyThemeContext from "../../themeContext";
const getFirstFridayDate = () => {
const today = new Date()
const today = new Date();
const month =
today.getDate() <= 7 && today.getDay() <= 5
? today.getMonth()
: today.getMonth() + 1
: today.getMonth() + 1;
let day = 1
let day = 1;
while (day <= 7) {
const dateOfVoting = new Date(
Date.UTC(today.getFullYear(), month, day, 15, 0, 0)
)
);
if (dateOfVoting.getDay() === 5) return dateOfVoting
if (dateOfVoting.getDay() === 5) return dateOfVoting;
day++
day++;
}
console.error("Couldn't resolve first friday of the month for voting")
return new Date(2000, 1, 1)
}
console.error("Couldn't resolve first friday of the month for voting");
return new Date(2000, 1, 1);
};
export const navIcons: {
code: string
displayName: string
code: string;
displayName: string;
menuItems: {
code: string
displayName: string
toAppend?: string
}[]
code: string;
displayName: string;
toAppend?: string;
}[];
}[] = [
{
code: "xsearch",
@ -52,6 +52,7 @@ export const navIcons: {
menuItems: [
{ code: "xsearch", displayName: "Browser" },
{ code: "xtrends", displayName: "Trends" },
{ code: "xleaderboards", displayName: "Leaderboards" },
],
},
//{ code: "sr", displayName: "Salmon Run", menuItems: [] },
@ -88,21 +89,21 @@ export const navIcons: {
{ code: "plus/faq", displayName: "FAQ" },
],
},
]
];
const IconNavBar = () => {
const { t } = useTranslation()
const { t } = useTranslation();
const {
darkerBgColor,
textColor,
themeColorWithShade,
grayWithShade,
} = useContext(MyThemeContext)
const location = useLocation()
} = useContext(MyThemeContext);
const location = useLocation();
const { data: plusInfoData } = useQuery<PlusInfoData>(PLUS_INFO)
const { data: plusInfoData } = useQuery<PlusInfoData>(PLUS_INFO);
const isVoting = !!plusInfoData?.plusInfo?.voting_ends
const isVoting = !!plusInfoData?.plusInfo?.voting_ends;
return (
<Flex bg={darkerBgColor} py={2} justifyContent="center" flexWrap="wrap">
@ -111,7 +112,7 @@ const IconNavBar = () => {
const codesTogether =
"/" +
code +
menuItems.reduce((acc, { code }) => acc + "/" + code, "")
menuItems.reduce((acc, { code }) => acc + "/" + code, "");
const MenuNavIcon = () => (
<Flex
flexDirection="column"
@ -145,13 +146,13 @@ const IconNavBar = () => {
</Box>
)}
</Flex>
)
);
if (!menuItems.length) {
return (
<Link key={code} to={code}>
<MenuNavIcon />
</Link>
)
);
}
return (
@ -168,8 +169,8 @@ const IconNavBar = () => {
>
{location.pathname === "/" + item.code ? (
<Box
h="5px"
w="5px"
h="7px"
w="7px"
mb={1}
bgColor={themeColorWithShade}
borderRadius="50%"
@ -179,8 +180,8 @@ const IconNavBar = () => {
/>
) : (
<Box
h="5px"
w="5px"
h="7px"
w="7px"
mb={1}
borderRadius="50%"
lineHeight="0.5rem"
@ -207,11 +208,11 @@ const IconNavBar = () => {
</MenuGroup>
</MenuList>
</Menu>
)
);
})}
</Suspense>
</Flex>
)
}
);
};
export default IconNavBar
export default IconNavBar;

View File

@ -5,11 +5,11 @@ import {
MenuItemOption,
MenuList,
MenuOptionGroup,
} from "@chakra-ui/core"
import React, { useContext } from "react"
import { useTranslation } from "react-i18next"
import { FiGlobe } from "react-icons/fi"
import MyThemeContext from "../../themeContext"
} from "@chakra-ui/core";
import React, { useContext } from "react";
import { useTranslation } from "react-i18next";
import { FiGlobe } from "react-icons/fi";
import MyThemeContext from "../../themeContext";
export const languages = [
{ code: "de", name: "Deutsch" },
@ -25,11 +25,11 @@ export const languages = [
{ code: "ja", name: "日本語" },
{ code: "ko", name: "한국어" },
{ code: "zh-TW", name: "繁體中文" },
] as const
] as const;
export const LanguageSelector = () => {
const { t, i18n } = useTranslation()
const { darkerBgColor, textColor } = useContext(MyThemeContext)
const { t, i18n } = useTranslation();
const { darkerBgColor, textColor } = useContext(MyThemeContext);
return (
<Menu>
@ -59,5 +59,5 @@ export const LanguageSelector = () => {
</MenuOptionGroup>
</MenuList>
</Menu>
)
}
);
};

View File

@ -1,24 +1,30 @@
import { Container, Flex } from "@chakra-ui/core"
import { useLocation } from "@reach/router"
import React, { Suspense, useContext, useEffect } from "react"
import MyThemeContext from "../../themeContext"
import Footer from "./Footer"
import IconNavBar from "./IconNavBar"
import TopNav from "./TopNav"
import { Container, Flex } from "@chakra-ui/core";
import { useLocation } from "@reach/router";
import React, { Suspense, useContext, useEffect } from "react";
import MyThemeContext from "../../themeContext";
import Footer from "./Footer";
import IconNavBar from "./IconNavBar";
import TopNav from "./TopNav";
interface LayoutProps {
children: React.ReactNode
children: React.ReactNode;
}
const PAGES_WITH_WIDE_CONTAINER = ["/analyzer", "/xsearch", "/builds", "/plans"]
const PAGES_WITH_WIDE_CONTAINER = [
"/analyzer",
"/xsearch",
"/builds",
"/plans",
"/xleaderboards",
];
const Layout: React.FC<LayoutProps> = ({ children }) => {
const { bgColor, textColor } = useContext(MyThemeContext)
const location = useLocation()
const { bgColor, textColor } = useContext(MyThemeContext);
const location = useLocation();
useEffect(() => {
document.body.style.backgroundColor = bgColor
}, [bgColor])
document.body.style.backgroundColor = bgColor;
}, [bgColor]);
return (
<>
@ -39,7 +45,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
<Footer />
</Flex>
</>
)
}
);
};
export default Layout
export default Layout;

View File

@ -1,35 +1,35 @@
import { useQuery } from "@apollo/react-hooks"
import { Box, Flex, Heading, Link } from "@chakra-ui/core"
import { RouteComponentProps } from "@reach/router"
import React, { useContext } from "react"
import { Helmet } from "react-helmet-async"
import { useTranslation } from "react-i18next"
import { LINKS } from "../../graphql/queries/links"
import MyThemeContext from "../../themeContext"
import Error from "../common/Error"
import Loading from "../common/Loading"
import PageHeader from "../common/PageHeader"
import Alert from "../elements/Alert"
import { useQuery } from "@apollo/client";
import { Box, Flex, Heading, Link } from "@chakra-ui/core";
import { RouteComponentProps } from "@reach/router";
import React, { useContext } from "react";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import { LINKS } from "../../graphql/queries/links";
import MyThemeContext from "../../themeContext";
import Error from "../common/Error";
import Loading from "../common/Loading";
import PageHeader from "../common/PageHeader";
import Alert from "../elements/Alert";
interface LinkI {
title: string
url: string
description: string
type: "GUIDE" | "DISCORD" | "MISC"
title: string;
url: string;
description: string;
type: "GUIDE" | "DISCORD" | "MISC";
}
interface LinksData {
links: LinkI[]
links: LinkI[];
}
const Links: React.FC<RouteComponentProps> = () => {
const { t, i18n } = useTranslation()
const { data, error, loading } = useQuery<LinksData>(LINKS)
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext)
const { t, i18n } = useTranslation();
const { data, error, loading } = useQuery<LinksData>(LINKS);
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext);
if (loading || !data) return <Loading />
if (error) return <Error errorMessage={error.message} />
const links = data.links
if (loading || !data) return <Loading />;
if (error) return <Error errorMessage={error.message} />;
const links = data.links;
const linkMap = (link: LinkI) => (
<React.Fragment key={link.title}>
@ -40,7 +40,7 @@ const Links: React.FC<RouteComponentProps> = () => {
{link.description}
</Box>
</React.Fragment>
)
);
return (
<>
@ -70,7 +70,7 @@ const Links: React.FC<RouteComponentProps> = () => {
{links.filter((link) => link.type === "MISC").map(linkMap)}
</Flex>
</>
)
}
);
};
export default Links
export default Links;

View File

@ -1,13 +1,13 @@
import React, { useContext } from "react"
import { RouteComponentProps } from "@reach/router"
import { Splatoon1Maps } from "../../assets/imageImports"
import { choose } from "../../utils/helperFunctions"
import { Image, Flex, Heading, Box } from "@chakra-ui/core"
import MyThemeContext from "../../themeContext"
import React, { useContext } from "react";
import { RouteComponentProps } from "@reach/router";
import { Splatoon1Maps } from "../../assets/imageImports";
import { choose } from "../../utils/helperFunctions";
import { Image, Flex, Heading, Box } from "@chakra-ui/core";
import MyThemeContext from "../../themeContext";
const NotFound: React.FC<RouteComponentProps> = () => {
const { grayWithShade } = useContext(MyThemeContext)
const mapObject = choose(Splatoon1Maps)
const { grayWithShade } = useContext(MyThemeContext);
const mapObject = choose(Splatoon1Maps);
return (
<Flex flexDirection="column" justifyContent="center" alignItems="center">
<Image src={mapObject.image} borderRadius="5px" w="500px" h="auto" />
@ -16,7 +16,7 @@ const NotFound: React.FC<RouteComponentProps> = () => {
...just like {mapObject.name} can't be found in Splatoon 2
</Box>
</Flex>
)
}
);
};
export default NotFound
export default NotFound;

View File

@ -1,36 +1,37 @@
import { Router } from "@reach/router"
import React, { lazy, Suspense } from "react"
import Loading from "../common/Loading"
import NotFound from "./NotFound"
import { ScrollToTop } from "./ScrollToTop"
import { Router } from "@reach/router";
import React, { lazy, Suspense } from "react";
import Loading from "../common/Loading";
import NotFound from "./NotFound";
import { ScrollToTop } from "./ScrollToTop";
const HomePage = lazy(() => import("../home/HomePage"))
const UserPage = lazy(() => import("../user/UserPage"))
const UserSearchPage = lazy(() => import("../usersearch/UserSearchPage"))
const MarkdownHelpPage = lazy(() => import("../markdown/MarkdownHelpPage"))
const BuildsPage = lazy(() => import("../builds/BuildsPage"))
const BuildAnalyzerPage = lazy(() => import("../analyzer/BuildAnalyzerPage"))
const CalendarPage = lazy(() => import("../calendar/CalendarPage"))
const TournamentsPage = lazy(() => import("../tournaments/TournamentsPage"))
const EventPage = lazy(() => import("../events/EventsPage"))
const HomePage = lazy(() => import("../home/HomePage"));
const UserPage = lazy(() => import("../user/UserPage"));
const UserSearchPage = lazy(() => import("../usersearch/UserSearchPage"));
const MarkdownHelpPage = lazy(() => import("../markdown/MarkdownHelpPage"));
const BuildsPage = lazy(() => import("../builds/BuildsPage"));
const BuildAnalyzerPage = lazy(() => import("../analyzer/BuildAnalyzerPage"));
const CalendarPage = lazy(() => import("../calendar/CalendarPage"));
const TournamentsPage = lazy(() => import("../tournaments/TournamentsPage"));
const EventPage = lazy(() => import("../events/EventsPage"));
const TournamentsDetailsPage = lazy(
() => import("../tournaments/TournamentDetailsPage")
)
const MapPlannerPage = lazy(() => import("../plans/MapPlannerPage"))
const FreeAgentsPage = lazy(() => import("../freeagents/FreeAgentsPage"))
const XSearch = lazy(() => import("../xsearch/Top500BrowserPage"))
const XTrends = lazy(() => import("../xtrends/XTrendsPage"))
const PlusPage = lazy(() => import("../plus/PlusPage"))
const PlusFAQPage = lazy(() => import("../plus/PlusFAQPage"))
const DraftCupPage = lazy(() => import("../plusdraftcup/DraftCupPage"))
const DraftCupDetails = lazy(() => import("../plusdraftcup/DraftCupDetails"))
const Access = lazy(() => import("./Access"))
const VotingHistoryPage = lazy(() => import("../plus/VotingHistoryPage"))
const VotingPage = lazy(() => import("../plus/VotingPage"))
const About = lazy(() => import("./About"))
const Links = lazy(() => import("./Links"))
const TranslatePage = lazy(() => import("../translate/TranslatePage"))
const AdminPage = lazy(() => import("../admin/AdminPage"))
);
const MapPlannerPage = lazy(() => import("../plans/MapPlannerPage"));
const FreeAgentsPage = lazy(() => import("../freeagents/FreeAgentsPage"));
const XSearch = lazy(() => import("../xsearch/Top500BrowserPage"));
const XTrends = lazy(() => import("../xtrends/XTrendsPage"));
const XLeaderboards = lazy(() => import("../xleaderboards/XLeaderboardsPage"));
const PlusPage = lazy(() => import("../plus/PlusPage"));
const PlusFAQPage = lazy(() => import("../plus/PlusFAQPage"));
const DraftCupPage = lazy(() => import("../plusdraftcup/DraftCupPage"));
const DraftCupDetails = lazy(() => import("../plusdraftcup/DraftCupDetails"));
const Access = lazy(() => import("./Access"));
const VotingHistoryPage = lazy(() => import("../plus/VotingHistoryPage"));
const VotingPage = lazy(() => import("../plus/VotingPage"));
const About = lazy(() => import("./About"));
const Links = lazy(() => import("./Links"));
const TranslatePage = lazy(() => import("../translate/TranslatePage"));
const AdminPage = lazy(() => import("../admin/AdminPage"));
const Routes: React.FC = () => {
return (
@ -53,6 +54,7 @@ const Routes: React.FC = () => {
<FreeAgentsPage path="/freeagents" />
<XSearch path="/xsearch" />
<XTrends path="/xtrends" />
<XLeaderboards path="/xleaderboards" />
<About path="/about" />
<Links path="/links" />
<Access path="/access" />
@ -66,7 +68,7 @@ const Routes: React.FC = () => {
</ScrollToTop>
</Router>
</Suspense>
)
}
);
};
export default Routes
export default Routes;

View File

@ -1,15 +1,15 @@
import React from "react"
import { RouteComponentProps } from "@reach/router"
import React from "react";
import { RouteComponentProps } from "@reach/router";
interface ScrollProps {
children: any
location?: any
children: any;
location?: any;
}
export const ScrollToTop: React.FC<ScrollProps & RouteComponentProps> = ({
children,
location,
}) => {
React.useLayoutEffect(() => window.scrollTo(0, 0), [location.pathname])
return children
}
React.useLayoutEffect(() => window.scrollTo(0, 0), [location.pathname]);
return children;
};

Some files were not shown because too many files have changed in this diff Show More