x trends first version

This commit is contained in:
Sendou 2020-04-11 01:27:59 +03:00
parent b40daa24d5
commit 842f9c06f4
12 changed files with 863 additions and 432 deletions

View File

@ -1991,9 +1991,9 @@
}
},
"@testing-library/jest-dom": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.3.0.tgz",
"integrity": "sha512-Cdhpc3BHL888X55qBNyra9eM0UG63LCm/FqCWTa1Ou/0MpsUbQTM9vW1NU6/jBQFoSLgkFfDG5XVpm2V0dOm/A==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.5.0.tgz",
"integrity": "sha512-7sWHrpxG4Yd8TmryI7Rtbx8Ff4mbs3ASye3oshQIuHvsCR+QHgr7rTR/PfeXvOmwUwR36wSTTAvrLKsPmr6VEQ==",
"requires": {
"@babel/runtime": "^7.9.2",
"@types/testing-library__jest-dom": "^5.0.2",
@ -2015,9 +2015,9 @@
}
},
"@jest/types": {
"version": "25.2.3",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.3.tgz",
"integrity": "sha512-6oLQwO9mKif3Uph3RX5J1i3S7X7xtDHWBaaaoeKw8hOzV6YUd0qDcYcHZ6QXMHDIzSr7zzrEa51o2Ovlj6AtKQ==",
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-25.3.0.tgz",
"integrity": "sha512-UkaDNewdqXAmCDbN2GlUM6amDKS78eCqiw/UmF5nE0mmLTd6moJkiZJML/X52Ke3LH7Swhw883IRXq8o9nWjVw==",
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^1.1.1",
@ -2065,9 +2065,9 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"diff-sequences": {
"version": "25.2.1",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.1.tgz",
"integrity": "sha512-foe7dXnGlSh3jR1ovJmdv+77VQj98eKCHHwJPbZ2eEf0fHwKbkZicpPxEch9smZ+n2dnF6QFwkOQdLq9hpeJUg=="
"version": "25.2.6",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz",
"integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg=="
},
"has-flag": {
"version": "4.0.0",
@ -2075,38 +2075,38 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"jest-diff": {
"version": "25.2.3",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.2.3.tgz",
"integrity": "sha512-VtZ6LAQtaQpFsmEzps15dQc5ELbJxy4L2DOSo2Ev411TUEtnJPkAMD7JneVypeMJQ1y3hgxN9Ao13n15FAnavg==",
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.3.0.tgz",
"integrity": "sha512-vyvs6RPoVdiwARwY4kqFWd4PirPLm2dmmkNzKqo38uZOzJvLee87yzDjIZLmY1SjM3XR5DwsUH+cdQ12vgqi1w==",
"requires": {
"chalk": "^3.0.0",
"diff-sequences": "^25.2.1",
"jest-get-type": "^25.2.1",
"pretty-format": "^25.2.3"
"diff-sequences": "^25.2.6",
"jest-get-type": "^25.2.6",
"pretty-format": "^25.3.0"
}
},
"jest-get-type": {
"version": "25.2.1",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz",
"integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w=="
"version": "25.2.6",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz",
"integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig=="
},
"jest-matcher-utils": {
"version": "25.2.3",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.2.3.tgz",
"integrity": "sha512-ZmiXiwQRVM9MoKjGMP5YsGGk2Th5ncyRxfXKz5AKsmU8m43kgNZirckVzaP61MlSa9LKmXbevdYqVp1ZKAw2Rw==",
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.3.0.tgz",
"integrity": "sha512-ZBUJ2fchNIZt+fyzkuCFBb8SKaU//Rln45augfUtbHaGyVxCO++ANARdBK9oPGXU3hEDgyy7UHnOP/qNOJXFUg==",
"requires": {
"chalk": "^3.0.0",
"jest-diff": "^25.2.3",
"jest-get-type": "^25.2.1",
"pretty-format": "^25.2.3"
"jest-diff": "^25.3.0",
"jest-get-type": "^25.2.6",
"pretty-format": "^25.3.0"
}
},
"pretty-format": {
"version": "25.2.3",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.3.tgz",
"integrity": "sha512-IP4+5UOAVGoyqC/DiomOeHBUKN6q00gfyT2qpAsRH64tgOKB2yF7FHJXC18OCiU0/YFierACup/zdCOWw0F/0w==",
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.3.0.tgz",
"integrity": "sha512-wToHwF8bkQknIcFkBqNfKu4+UZqnrLn/Vr+wwKQwwvPzkBfDDKp/qIabFqdgtoi5PEnM8LFByVsOrHoa3SpTVA==",
"requires": {
"@jest/types": "^25.2.3",
"@jest/types": "^25.3.0",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.0.0",
"react-is": "^16.12.0"
@ -2392,9 +2392,9 @@
}
},
"@types/react": {
"version": "16.9.32",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.32.tgz",
"integrity": "sha512-fmejdp0CTH00mOJmxUPPbWCEBWPvRIL4m8r0qD+BSDUqmutPyGQCHifzMpMzdvZwROdEdL78IuZItntFWgPXHQ==",
"version": "16.9.34",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.34.tgz",
"integrity": "sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==",
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
@ -2565,9 +2565,9 @@
}
},
"@types/testing-library__jest-dom": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.0.2.tgz",
"integrity": "sha512-dZP+/WHndgCSmdaImITy0KhjGAa9c0hlGGkzefbtrPFpnGEPZECDA0zyvfSp8RKhHECJJSKHFExjOwzo0rHyIA==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.0.3.tgz",
"integrity": "sha512-NdbKc6yseg6uq4UJFwimPws0iwsGugVbPoOTP2EH+PJMJKiZsoSg5F2H3XYweOyytftCOuIMuXifBUrF9CSvaQ==",
"requires": {
"@types/jest": "*"
}

View File

@ -10,12 +10,12 @@
"@reach/router": "^1.3.3",
"@rehooks/local-storage": "^2.3.0",
"@sendou/react-sketch": "^0.5.2",
"@testing-library/jest-dom": "^5.3.0",
"@testing-library/jest-dom": "^5.5.0",
"@testing-library/react": "^10.0.2",
"@testing-library/user-event": "^10.0.1",
"@types/jest": "^25.2.1",
"@types/reach__router": "^1.3.4",
"@types/react": "^16.9.32",
"@types/react": "^16.9.34",
"@types/react-color": "^3.0.1",
"@types/react-datepicker": "^2.11.0",
"@types/react-dom": "^16.9.6",

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -58,7 +58,7 @@ const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
<Box display="flex" flexDirection="column" h="100%">
<Box display="flex" justifyContent="space-between">
<Box width="24">
<WeaponImage englishName={build.weapon} size="MEDIUM" />
<WeaponImage englishName={build.weapon} size="BIG" />
</Box>
{build.top && (
<Image

View File

@ -6,22 +6,35 @@ import { Weapon } from "../../types.js"
interface WeaponImageProps {
englishName: Weapon
size: "SMALL" | "MEDIUM"
size: "SMALL" | "MEDIUM" | "BIG"
asInlineBlock?: boolean
noTitle?: boolean
}
const WeaponImage: React.FC<WeaponImageProps> = ({ englishName, size }) => {
const sizeWhMap: Record<"SMALL" | "MEDIUM" | "BIG", string | undefined> = {
SMALL: "32px",
MEDIUM: "64px",
BIG: undefined,
}
const WeaponImage: React.FC<WeaponImageProps> = ({
englishName,
size,
asInlineBlock,
noTitle,
}) => {
const dictToUse: any = wpnMedium
const wh = "32px"
const wh = sizeWhMap[size]
return (
<img
src={dictToUse[english_internal[englishName]]}
alt={englishName}
title={englishName}
style={
size === "SMALL"
? { width: wh, height: wh, display: "inline-block" }
: undefined
}
title={noTitle ? undefined : englishName}
style={{
width: wh,
height: wh,
display: asInlineBlock ? "inline-block" : undefined,
}}
/>
)
}

View File

@ -68,7 +68,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
Build
>(ADD_BUILD, {
variables: { ...(build as Build) },
onCompleted: data => {
onCompleted: (data) => {
closeModal()
toast({
description: `New ${data.addBuild.weapon} build created`,
@ -77,7 +77,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
duration: 10000,
})
},
onError: error => {
onError: (error) => {
toast({
title: "An error occurred",
description: error.message,
@ -103,7 +103,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
duration: 10000,
})
},
onError: error => {
onError: (error) => {
toast({
title: "An error occurred",
description: error.message,
@ -129,7 +129,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
duration: 10000,
})
},
onError: error => {
onError: (error) => {
toast({
title: "An error occurred",
description: error.message,
@ -235,9 +235,9 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
}
const abilitiesFilled =
build.headgear?.every(ability => ability !== "UNKNOWN") &&
build.clothing?.every(ability => ability !== "UNKNOWN") &&
build.shoes?.every(ability => ability !== "UNKNOWN")
build.headgear?.every((ability) => ability !== "UNKNOWN") &&
build.clothing?.every((ability) => ability !== "UNKNOWN") &&
build.shoes?.every((ability) => ability !== "UNKNOWN")
if (!abilitiesFilled) {
return false
@ -269,7 +269,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
value={build.weapon}
/>
{build.weapon && (
<WeaponImage englishName={build.weapon as Weapon} size="MEDIUM" />
<WeaponImage englishName={build.weapon as Weapon} size="BIG" />
)}
<Box asFlex mt="1em" justifyContent="space-between" flexWrap="wrap">
<Box asFlex flexDirection="column" alignItems="center">
@ -283,7 +283,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
setValue={(headgearItem: HeadGear) => {
if (
(!build.headgear ||
build.headgear.every(ability => ability === "UNKNOWN")) &&
build.headgear.every((ability) => ability === "UNKNOWN")) &&
existingGear.hasOwnProperty(headgearItem)
) {
handleChange({
@ -313,7 +313,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
setValue={(clothingItem: ClothingGear) => {
if (
(!build.clothing ||
build.clothing.every(ability => ability === "UNKNOWN")) &&
build.clothing.every((ability) => ability === "UNKNOWN")) &&
existingGear.hasOwnProperty(clothingItem)
) {
handleChange({
@ -343,7 +343,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
setValue={(shoesItem: ShoesGear) => {
if (
(!build.shoes ||
build.shoes.every(ability => ability === "UNKNOWN")) &&
build.shoes.every((ability) => ability === "UNKNOWN")) &&
existingGear.hasOwnProperty(shoesItem)
) {
handleChange({
@ -376,7 +376,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
</Box>
<Box mt="1em">
<AbilityButtons
onClick={ability => handleAbilityButtonClick(ability)}
onClick={(ability) => handleAbilityButtonClick(ability)}
/>
</Box>
<Box mt="1em">

View File

@ -0,0 +1,102 @@
import React from "react"
import { Flex, Box, Image, PseudoBox } from "@chakra-ui/core"
import sz from "../../assets/sz.png"
import tc from "../../assets/tc.png"
import rm from "../../assets/rm.png"
import cb from "../../assets/cb.png"
interface ModeButtonsProps {
mode: "SZ" | "TC" | "RM" | "CB"
setMode: React.Dispatch<React.SetStateAction<"SZ" | "TC" | "RM" | "CB">>
}
const iconSize = "45px"
const ModeButtons: React.FC<ModeButtonsProps> = ({ mode, setMode }) => {
return (
<Flex>
<Flex
flexDir="column"
p="10px"
style={{ filter: mode === "SZ" ? undefined : "grayscale(100%)" }}
cursor="pointer"
onClick={() => setMode("SZ")}
alignItems="center"
>
<PseudoBox
_hover={{ transform: mode === "SZ" ? undefined : "scale(1.2)" }}
transition="all 0.2s"
>
<Image src={sz} display="inline-block" w={iconSize} h={iconSize} />
</PseudoBox>
{mode === "SZ" && (
<Box fontSize="1.25em" fontWeight="bold">
SZ
</Box>
)}
</Flex>
<Flex
flexDir="column"
p="10px"
style={{ filter: mode === "TC" ? undefined : "grayscale(100%)" }}
cursor="pointer"
onClick={() => setMode("TC")}
alignItems="center"
>
<PseudoBox
_hover={{ transform: mode === "TC" ? undefined : "scale(1.2)" }}
transition="all 0.2s"
>
<Image src={tc} display="inline-block" w={iconSize} h={iconSize} />
</PseudoBox>
{mode === "TC" && (
<Box fontSize="1.25em" fontWeight="bold">
TC
</Box>
)}
</Flex>
<Flex
flexDir="column"
p="10px"
style={{ filter: mode === "RM" ? undefined : "grayscale(100%)" }}
cursor="pointer"
onClick={() => setMode("RM")}
alignItems="center"
>
<PseudoBox
_hover={{ transform: mode === "RM" ? undefined : "scale(1.2)" }}
transition="all 0.2s"
>
<Image src={rm} display="inline-block" w={iconSize} h={iconSize} />
</PseudoBox>
{mode === "RM" && (
<Box fontSize="1.25em" fontWeight="bold">
RM
</Box>
)}
</Flex>
<Flex
flexDir="column"
p="10px"
style={{ filter: mode === "CB" ? undefined : "grayscale(100%)" }}
cursor="pointer"
onClick={() => setMode("CB")}
alignItems="center"
>
<PseudoBox
_hover={{ transform: mode === "CB" ? undefined : "scale(1.2)" }}
transition="all 0.2s"
>
<Image src={cb} display="inline-block" w={iconSize} h={iconSize} />
</PseudoBox>
{mode === "CB" && (
<Box fontSize="1.25em" fontWeight="bold">
CB
</Box>
)}
</Flex>
</Flex>
)
}
export default ModeButtons

View File

@ -1,24 +1,200 @@
import React from "react"
import React, { useEffect, useState, useContext } from "react"
import { RouteComponentProps } from "@reach/router"
import { useQuery } from "@apollo/react-hooks"
import { XTrendsData, X_TRENDS } from "../../graphql/queries/xTrends"
import Loading from "../common/Loading"
import Error from "../common/Error"
import PageHeader from "../common/PageHeader"
import { months, weapons } from "../../utils/lists"
import { Weapon } from "../../types"
import {
Flex,
Box,
Popover,
PopoverTrigger,
PopoverContent,
PopoverArrow,
} from "@chakra-ui/core"
import WeaponImage from "../common/WeaponImage"
import MyThemeContext from "../../themeContext"
import ModeButtons from "./ModeButtons"
import Select from "../elements/Select"
interface XTrendsPageProps {}
const tiers = [
{
label: "X",
criteria: 6,
color: "purple.700",
},
{
label: "S+",
criteria: 5,
color: "red.700",
},
{
label: "S",
criteria: 4,
color: "red.700",
},
{
label: "A+",
criteria: 3,
color: "orange.700",
},
{
label: "A",
criteria: 2,
color: "orange.700",
},
{
label: "B+",
criteria: 1.5,
color: "yellow.700",
},
{
label: "B",
criteria: 1,
color: "yellow.700",
},
{
label: "C+",
criteria: 0.5,
color: "green.700",
},
{
label: "C",
criteria: 0.002, //1 in 500
color: "green.700",
},
] as const
const XTrendsPage: React.FC<RouteComponentProps> = () => {
const { grayWithShade, darkerBgColor } = useContext(MyThemeContext)
const { data, error, loading } = useQuery<XTrendsData>(X_TRENDS)
const [month, setMonth] = useState("March 2020")
const [mode, setMode] = useState<"SZ" | "TC" | "RM" | "CB">("SZ")
const [weaponMonths, setWeaponMonths] = useState<Record<
string,
Record<"SZ" | "TC" | "RM" | "CB", { name: Weapon; amount: number }[]>
> | null>(null)
useEffect(() => {
if (loading || error || !!weaponMonths) return
const newWeaponMonths: Record<
string,
Record<"SZ" | "TC" | "RM" | "CB", { name: Weapon; amount: number }[]>
> = {}
data!.xTrends.forEach((weaponObj) => {
weaponObj.counts.forEach((count) => {
const arrays = [count.SZ, count.TC, count.RM, count.CB]
arrays.forEach((arr, modeIndex) => {
const modes = ["SZ", "TC", "RM", "CB"] as const
const mode = modes[modeIndex]
arr.forEach((num, numIndex) => {
if (num === null || num === 0) return
const monthString = `${months[numIndex]} ${count.year}`
if (!newWeaponMonths[monthString]) {
newWeaponMonths[monthString] = { SZ: [], TC: [], RM: [], CB: [] }
}
let weaponMonth = newWeaponMonths[monthString][mode]
weaponMonth.push({ name: weaponObj.weapon as Weapon, amount: num })
newWeaponMonths[monthString][mode] = weaponMonth
})
})
})
})
setWeaponMonths(newWeaponMonths)
}, [data, loading, error, weaponMonths])
if (error) return <Error errorMessage={error.message} />
if (loading) return <Loading />
if (loading || !weaponMonths) return <Loading />
const weaponsOrdered = weaponMonths[month][mode].sort((a, b) => {
const comparision = b.amount - a.amount
if (comparision !== 0) return comparision
return weapons.indexOf(a.name) - weapons.indexOf(b.name)
})
console.log("weaponMonths", weaponMonths)
console.log("xtrends", data!.xTrends)
return (
<>
<PageHeader title="X Trends" />
{data!.xTrends.length}
<Select
value={month}
setValue={(value) => setMonth(value)}
options={Object.keys(weaponMonths)
.sort((a, b) => {
const partsA = a.split(" ")
const partsB = b.split(" ")
if (partsA[1] !== partsB[1]) {
return parseInt(partsB[1]) - parseInt(partsA[1])
}
return (
months.indexOf(partsB[0] as any) -
months.indexOf(partsA[0] as any)
)
})
.map((month) => ({ label: month, value: month }))}
/>
<Box my="1em">
<ModeButtons mode={mode} setMode={setMode} />
</Box>
{tiers.map((tier, index, tiers) => (
<Flex key={tier.criteria}>
<Flex
flexDir="column"
w="100px"
minH="100px"
padding="10px"
borderRight="5px solid"
borderColor={tier.color}
marginRight="1em"
justifyContent="center"
>
<Box fontSize="2em" fontWeight="bolder">
{tier.label}
</Box>
<Box color={grayWithShade}>
{tier.criteria === 0.002 ? "At least 1" : `${tier.criteria}%`}
</Box>
</Flex>
<Flex flexDir="row" flex={1} flexWrap="wrap" alignItems="center">
{weaponsOrdered
.filter((weapon) => {
const previousCriteria =
index === 0 ? 101 : tiers[index - 1].criteria
const percentsInTop500 = weapon.amount / 5
return (
percentsInTop500 < previousCriteria &&
percentsInTop500 >= tier.criteria
)
})
.map((weapon) => (
<Popover key={weapon.name} placement="top-start">
<PopoverTrigger>
<Box mx="0.25em">
<WeaponImage
englishName={weapon.name}
size="MEDIUM"
noTitle
/>
</Box>
</PopoverTrigger>
<PopoverContent zIndex={4} p="0.5em" bg={darkerBgColor}>
<PopoverArrow />
{weapon.name} - {weapon.amount}
</PopoverContent>
</Popover>
))}
</Flex>
</Flex>
))}
</>
)
}

View File

@ -5,10 +5,10 @@ export interface XTrendsData {
weapon: string
counts: {
year: number
SZ: number[]
TC: number[]
RM: number[]
CB: number[]
SZ: (null | number)[]
TC: (null | number)[]
RM: (null | number)[]
CB: (null | number)[]
}[]
}[]
}

868
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,6 @@
},
"devDependencies": {
"cross-env": "^7.0.2",
"nodemon": "^2.0.2"
"nodemon": "^2.0.3"
}
}