mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 23:19:39 -05:00
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:
parent
a92c2546b6
commit
710d99cab2
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": false
|
||||
}
|
||||
9
frontend-react/codegen.yml
Normal file
9
frontend-react/codegen.yml
Normal 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"
|
||||
3065
frontend-react/package-lock.json
generated
3065
frontend-react/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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={<>«</>}
|
||||
nextLabel={<>»</>}
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
91
frontend-react/src/components/common/Table.tsx
Normal file
91
frontend-react/src/components/common/Table.tsx
Normal 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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue
Block a user