mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-15 07:23:06 -05:00
x search and x trends
This commit is contained in:
parent
e9f3924d9d
commit
e5d85eced4
196
react-ui/package-lock.json
generated
196
react-ui/package-lock.json
generated
|
|
@ -4113,6 +4113,73 @@
|
|||
"type": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
|
||||
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
||||
},
|
||||
"d3-collection": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
|
||||
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
|
||||
},
|
||||
"d3-color": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.0.tgz",
|
||||
"integrity": "sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg=="
|
||||
},
|
||||
"d3-format": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.1.tgz",
|
||||
"integrity": "sha512-TUswGe6hfguUX1CtKxyG2nymO+1lyThbkS1ifLX0Sr+dOQtAD5gkrffpHnx+yHNKUZ0Bmg5T4AjUQwugPDrm0g=="
|
||||
},
|
||||
"d3-interpolate": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz",
|
||||
"integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==",
|
||||
"requires": {
|
||||
"d3-color": "1"
|
||||
}
|
||||
},
|
||||
"d3-path": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz",
|
||||
"integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg=="
|
||||
},
|
||||
"d3-scale": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
|
||||
"integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
|
||||
"requires": {
|
||||
"d3-array": "^1.2.0",
|
||||
"d3-collection": "1",
|
||||
"d3-format": "1",
|
||||
"d3-interpolate": "1",
|
||||
"d3-time": "1",
|
||||
"d3-time-format": "2"
|
||||
}
|
||||
},
|
||||
"d3-shape": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz",
|
||||
"integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==",
|
||||
"requires": {
|
||||
"d3-path": "1"
|
||||
}
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
|
||||
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz",
|
||||
"integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
}
|
||||
},
|
||||
"damerau-levenshtein": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz",
|
||||
|
|
@ -4166,6 +4233,11 @@
|
|||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
||||
},
|
||||
"decimal.js-light": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.0.tgz",
|
||||
"integrity": "sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg=="
|
||||
},
|
||||
"decode-uri-component": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||
|
|
@ -4402,6 +4474,14 @@
|
|||
"utila": "~0.4"
|
||||
}
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
|
||||
"integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2"
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz",
|
||||
|
|
@ -8137,6 +8217,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
|
||||
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="
|
||||
},
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
|
|
@ -8164,6 +8249,11 @@
|
|||
"lodash._reinterpolate": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"lodash.unescape": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
|
||||
|
|
@ -8260,6 +8350,11 @@
|
|||
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||
},
|
||||
"math-expression-evaluator": {
|
||||
"version": "1.2.17",
|
||||
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
|
||||
"integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw="
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
|
|
@ -10618,6 +10713,11 @@
|
|||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.1.tgz",
|
||||
"integrity": "sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw=="
|
||||
},
|
||||
"react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-popper": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.4.tgz",
|
||||
|
|
@ -10631,6 +10731,17 @@
|
|||
"warning": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"react-resize-detector": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-2.3.0.tgz",
|
||||
"integrity": "sha512-oCAddEWWeFWYH5FAcHdBYcZjAw9fMzRUK9sWSx6WvSSOPVRxcHd5zTIGy/mOus+AhN/u6T4TMiWxvq79PywnJQ==",
|
||||
"requires": {
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"prop-types": "^15.6.0",
|
||||
"resize-observer-polyfill": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz",
|
||||
|
|
@ -10748,6 +10859,28 @@
|
|||
"react-dom": "^16.6.1"
|
||||
}
|
||||
},
|
||||
"react-smooth": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-1.0.2.tgz",
|
||||
"integrity": "sha512-pIGzL1g9VGAsRsdZQokIK0vrCkcdKtnOnS1gyB2rrowdLy69lNSWoIjCTWAfgbiYvria8tm5hEZqj+jwXMkV4A==",
|
||||
"requires": {
|
||||
"lodash": "~4.17.4",
|
||||
"prop-types": "^15.6.0",
|
||||
"raf": "^3.4.0",
|
||||
"react-transition-group": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
|
||||
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
|
||||
"requires": {
|
||||
"dom-helpers": "^3.4.0",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-lifecycles-compat": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"reactcss": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||
|
|
@ -10827,6 +10960,39 @@
|
|||
"util.promisify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"recharts": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-1.7.1.tgz",
|
||||
"integrity": "sha512-i4vK/ZSICr+dXGmaijuNybc+xhctiX0464xnqauY+OvE6WvU5v+0GYciQvD/HJSObkKG4wY8aRtiuUL9YtXnHQ==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.5",
|
||||
"core-js": "^2.5.1",
|
||||
"d3-interpolate": "^1.3.0",
|
||||
"d3-scale": "^2.1.0",
|
||||
"d3-shape": "^1.2.0",
|
||||
"lodash": "^4.17.5",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-resize-detector": "^2.3.0",
|
||||
"react-smooth": "^1.0.0",
|
||||
"recharts-scale": "^0.4.2",
|
||||
"reduce-css-calc": "^1.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz",
|
||||
"integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"recharts-scale": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.2.tgz",
|
||||
"integrity": "sha512-p/cKt7j17D1CImLgX2f5+6IXLbRHGUQkogIp06VUoci/XkhOQiGSzUrsD1uRmiI7jha4u8XNFOjkHkzzBPivMg==",
|
||||
"requires": {
|
||||
"decimal.js-light": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"recursive-readdir": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
||||
|
|
@ -10835,6 +11001,31 @@
|
|||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"reduce-css-calc": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
|
||||
"integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=",
|
||||
"requires": {
|
||||
"balanced-match": "^0.4.2",
|
||||
"math-expression-evaluator": "^1.2.14",
|
||||
"reduce-function-call": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
|
||||
"integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
|
||||
}
|
||||
}
|
||||
},
|
||||
"reduce-function-call": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.3.tgz",
|
||||
"integrity": "sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
|
||||
|
|
@ -11064,6 +11255,11 @@
|
|||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||
},
|
||||
"resize-observer-polyfill": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
|
||||
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "3.1.2",
|
||||
"react-sketch": "^0.5.1",
|
||||
"recharts": "^1.7.1",
|
||||
"semantic-ui-css": "^2.4.1",
|
||||
"semantic-ui-react": "^0.88.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { weapons } from "../../utils/lists"
|
|||
import { wpnSmall } from "../../assets/imageImports"
|
||||
import weaponDict from "../../utils/english_internal.json"
|
||||
|
||||
const WeaponDropdown = ({ onChange, value, showImages = true }) => {
|
||||
const WeaponDropdown = ({ value, onChange, showImages = true }) => {
|
||||
return (
|
||||
<Dropdown
|
||||
placeholder="Choose a weapon"
|
||||
|
|
|
|||
24
react-ui/src/components/root/Footer.js
vendored
24
react-ui/src/components/root/Footer.js
vendored
|
|
@ -26,17 +26,29 @@ const Footer = () => {
|
|||
<Grid.Column width={3}>
|
||||
<Header inverted as="h4" content="Follow Sendou" />
|
||||
<List link inverted>
|
||||
<List.Item as="a">Twitter</List.Item>
|
||||
<List.Item as="a">Twitch</List.Item>
|
||||
<List.Item as="a">YouTube</List.Item>
|
||||
<List.Item as="a">Discord</List.Item>
|
||||
<List.Item as="a" href="https://twitter.com/sendouc">
|
||||
Twitter
|
||||
</List.Item>
|
||||
<List.Item as="a" href="https://www.twitch.tv/sendou">
|
||||
Twitch
|
||||
</List.Item>
|
||||
<List.Item as="a" href="https://www.youtube.com/sendou">
|
||||
YouTube
|
||||
</List.Item>
|
||||
<List.Item as="a" href="https://discordapp.com/invite/J6NqUvt">
|
||||
Discord
|
||||
</List.Item>
|
||||
</List>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={3}>
|
||||
<Header inverted as="h4" content="Splatoon elsewhere" />
|
||||
<List link inverted>
|
||||
<List.Item as="a">Splatoonwiki</List.Item>
|
||||
<List.Item as="a">Some random</List.Item>
|
||||
<List.Item as="a" href="https://splatoonwiki.org/">
|
||||
Inkipedia
|
||||
</List.Item>
|
||||
<List.Item as="a" href="https://stat.ink/">
|
||||
stat.ink
|
||||
</List.Item>
|
||||
<List.Item as="a">Links</List.Item>
|
||||
<List.Item as="a">More</List.Item>
|
||||
</List>
|
||||
|
|
|
|||
12
react-ui/src/components/root/MainMenu.js
vendored
12
react-ui/src/components/root/MainMenu.js
vendored
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react"
|
||||
import { Menu, Container, Image, Dropdown, Button } from "semantic-ui-react"
|
||||
import { NavLink } from "react-router-dom"
|
||||
import { Menu, Container, Image, Dropdown, Icon } from "semantic-ui-react"
|
||||
import { Link, NavLink } from "react-router-dom"
|
||||
import sink_logo from "../../assets/sink_logo.png"
|
||||
|
||||
const dropdownStyle = {
|
||||
|
|
@ -11,7 +11,7 @@ const dropdownStyle = {
|
|||
|
||||
const MainMenu = () => {
|
||||
return (
|
||||
<Menu inverted attached="top" stackable>
|
||||
<Menu inverted secondary attached="top" stackable>
|
||||
<Container>
|
||||
<Menu.Item as="a" header>
|
||||
<Image src={sink_logo} style={{ height: "40px", width: "auto" }} />
|
||||
|
|
@ -53,8 +53,10 @@ const MainMenu = () => {
|
|||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
<Menu.Item position="right">
|
||||
<Button style={{ background: "#7289DA" }}>Log in via Discord</Button>
|
||||
|
||||
<Menu.Item as={Link} to="/" position="right">
|
||||
<Icon name="discord" size="large" style={{ paddingRight: "0.2em" }} />
|
||||
Log in via Discord
|
||||
</Menu.Item>
|
||||
</Container>
|
||||
</Menu>
|
||||
|
|
|
|||
32
react-ui/src/components/root/RollSim.js
vendored
32
react-ui/src/components/root/RollSim.js
vendored
|
|
@ -1,10 +1,8 @@
|
|||
import React, { useState } from "react"
|
||||
import { Comment } from "semantic-ui-react"
|
||||
import { clothingGear, shoesGear, headGear } from "../../utils/lists"
|
||||
import { choose } from "../../utils/helperFunctions"
|
||||
|
||||
import { abilityIcons } from "../../assets/imageImports"
|
||||
import murchpfp from "../../assets/murchpfp.png"
|
||||
import head from "../../utils/head.json"
|
||||
import clothes from "../../utils/clothes.json"
|
||||
import shoes from "../../utils/shoes.json"
|
||||
|
|
@ -37,6 +35,8 @@ const subAbilityStyle = {
|
|||
}
|
||||
const gearStyle = { maxWidth: "50px", height: "auto" }
|
||||
|
||||
const paddingStyle = { paddingLeft: "0.7em" }
|
||||
|
||||
const subAbilities = [
|
||||
abilityIcons.ISM,
|
||||
abilityIcons.ISS,
|
||||
|
|
@ -167,7 +167,6 @@ const RollSim = () => {
|
|||
choose(subAbilities)
|
||||
])
|
||||
const [rolling, setRolling] = useState(false)
|
||||
const [rollCount, setRollCount] = useState(0)
|
||||
|
||||
const setSubs = (json, gear) => {
|
||||
let gearName = gear.split("_")[1]
|
||||
|
|
@ -239,7 +238,6 @@ const RollSim = () => {
|
|||
}
|
||||
setSubsState(setSubs(json, gear)) //only using this method in the last roll that matters for optimization purposes
|
||||
setRolling(false)
|
||||
setRollCount(rollCount + 1)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -250,7 +248,9 @@ const RollSim = () => {
|
|||
style={gearStyle}
|
||||
alt=""
|
||||
/>
|
||||
<img src={headMain} style={mainAbilityStyle} alt="" />
|
||||
<span style={paddingStyle}>
|
||||
<img src={headMain} style={mainAbilityStyle} alt="" />
|
||||
</span>
|
||||
<img
|
||||
src={headSubs[0]}
|
||||
style={subAbilityStyle}
|
||||
|
|
@ -276,7 +276,9 @@ const RollSim = () => {
|
|||
style={gearStyle}
|
||||
alt=""
|
||||
/>
|
||||
<img src={clothingMain} style={mainAbilityStyle} alt="" />
|
||||
<span style={paddingStyle}>
|
||||
<img src={clothingMain} style={mainAbilityStyle} alt="" />
|
||||
</span>
|
||||
<img
|
||||
src={clothingSubs[0]}
|
||||
style={subAbilityStyle}
|
||||
|
|
@ -302,7 +304,9 @@ const RollSim = () => {
|
|||
style={gearStyle}
|
||||
alt=""
|
||||
/>
|
||||
<img src={shoesMain} style={mainAbilityStyle} alt="" />
|
||||
<span style={paddingStyle}>
|
||||
<img src={shoesMain} style={mainAbilityStyle} alt="" />
|
||||
</span>
|
||||
<img
|
||||
src={shoesSubs[0]}
|
||||
style={subAbilityStyle}
|
||||
|
|
@ -322,20 +326,6 @@ const RollSim = () => {
|
|||
onClick={() => roll("SHOES")}
|
||||
/>
|
||||
</div>
|
||||
{rollCount > 0 && (
|
||||
<Comment.Group>
|
||||
<Comment>
|
||||
<Comment.Avatar as="a" src={murchpfp} />
|
||||
<Comment.Content>
|
||||
<Comment.Author style={{ color: "white" }}>Murch</Comment.Author>
|
||||
<Comment.Text style={{ color: "white" }}>
|
||||
You have rolled {rollCount} {rollCount === 1 ? "time" : "times"}
|
||||
, chum.
|
||||
</Comment.Text>
|
||||
</Comment.Content>
|
||||
</Comment>
|
||||
</Comment.Group>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
8
react-ui/src/components/root/Routes.js
vendored
8
react-ui/src/components/root/Routes.js
vendored
|
|
@ -7,6 +7,8 @@ const Rotations = lazy(() => import("../rotation/Rotations"))
|
|||
const MapPlanner = lazy(() => import("../plans/MapPlanner"))
|
||||
const Calendar = lazy(() => import("../calendar/Calendar"))
|
||||
const XLeaderboard = lazy(() => import("../xleaderboard/XLeaderboard"))
|
||||
const PlayerXRankStats = lazy(() => import("../xsearch/PlayerXRankStats"))
|
||||
const XTrends = lazy(() => import("../xtrends/XTrends"))
|
||||
|
||||
const Routes = () => {
|
||||
return (
|
||||
|
|
@ -27,6 +29,12 @@ const Routes = () => {
|
|||
<Route path="/xleaderboard">
|
||||
<XLeaderboard />
|
||||
</Route>
|
||||
<Route path="/xsearch/p/:uid">
|
||||
<PlayerXRankStats />
|
||||
</Route>
|
||||
<Route path="/trends">
|
||||
<XTrends />
|
||||
</Route>
|
||||
</Switch>
|
||||
</Suspense>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const Rotations = ({ setMenuSelection }) => {
|
|||
}, [data, loading, monthly.loading])
|
||||
|
||||
if (loading || monthly.loading || rotation.length === 0) return <Loading />
|
||||
if (error || monthly.error) return <Error />
|
||||
if (error || monthly.error) return <Error errorMessage={error.message} />
|
||||
|
||||
const monthlyMaps = monthly.data.maplists[0]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect } from "react"
|
||||
import { Table, Icon, Popup, Segment } from "semantic-ui-react"
|
||||
import { Table, Icon, Popup } from "semantic-ui-react"
|
||||
import { useQuery } from "@apollo/react-hooks"
|
||||
import { Link } from "react-router-dom"
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ const FlexLeaderboard = () => {
|
|||
)
|
||||
}
|
||||
if (result.error) {
|
||||
return <Error />
|
||||
return <Error errorMessage={result.error.message} />
|
||||
}
|
||||
const leaderboard = result.data["topFlex"]
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const WeaponLeaderboard = ({ query, queryName, scoreField, weaponsField }) => {
|
|||
)
|
||||
}
|
||||
if (result.error) {
|
||||
return <Error />
|
||||
return <Error errorMessage={result.error.message} />
|
||||
}
|
||||
|
||||
const leaderboard = result.data[queryName]
|
||||
|
|
|
|||
61
react-ui/src/components/xsearch/MonthsTable.js
vendored
Normal file
61
react-ui/src/components/xsearch/MonthsTable.js
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import React from "react"
|
||||
import { Table, Header, Image } from "semantic-ui-react"
|
||||
|
||||
import { wpnSmall } from "../../assets/imageImports"
|
||||
import szIcon from "../../assets/sz.png"
|
||||
import tcIcon from "../../assets/tc.png"
|
||||
import rmIcon from "../../assets/rm.png"
|
||||
import cbIcon from "../../assets/cb.png"
|
||||
import { months } from "../../utils/lists"
|
||||
import weaponDict from "../../utils/english_internal.json"
|
||||
|
||||
const MonthsTable = ({ placements }) => {
|
||||
//data received is ordered chronologically and sz->tc->rm->cb
|
||||
const modeIcons = [null, szIcon, tcIcon, rmIcon, cbIcon]
|
||||
let lastMonth = placements[0].month
|
||||
let toggle = false
|
||||
|
||||
return (
|
||||
<Table basic="very" celled collapsing>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell></Table.HeaderCell>
|
||||
<Table.HeaderCell>Name</Table.HeaderCell>
|
||||
<Table.HeaderCell>X Power</Table.HeaderCell>
|
||||
<Table.HeaderCell>Placement</Table.HeaderCell>
|
||||
<Table.HeaderCell>Weapon</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
|
||||
<Table.Body>
|
||||
{placements.map(p => {
|
||||
if (lastMonth !== p.month) {
|
||||
lastMonth = p.month
|
||||
toggle = !toggle
|
||||
}
|
||||
return (
|
||||
<Table.Row key={p.id} active={toggle}>
|
||||
<Table.Cell>
|
||||
<Header as="h4" image>
|
||||
<Image src={modeIcons[p.mode]} rounded size="mini" />
|
||||
<Header.Content>
|
||||
{months[p.month]}
|
||||
<Header.Subheader>{p.year}</Header.Subheader>
|
||||
</Header.Content>
|
||||
</Header>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{p.name}</Table.Cell>
|
||||
<Table.Cell>{p.x_power}</Table.Cell>
|
||||
<Table.Cell>{p.rank}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<img src={wpnSmall[weaponDict[p.weapon]]} alt={p.weapon} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
export default MonthsTable
|
||||
146
react-ui/src/components/xsearch/PlayerXRankStats.js
vendored
Normal file
146
react-ui/src/components/xsearch/PlayerXRankStats.js
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import React, { useEffect, useState } from "react"
|
||||
import { useQuery } from "@apollo/react-hooks"
|
||||
import { Header, Image, Icon, List, Segment } from "semantic-ui-react"
|
||||
import { Link, Redirect, useParams } from "react-router-dom"
|
||||
|
||||
import { playerInfo } from "../../graphql/queries/playerInfo"
|
||||
import TopPlacementsTable from "./TopPlacementsTable"
|
||||
import WpnPlayedTable from "./WpnPlayedTable"
|
||||
import MonthsTable from "./MonthsTable"
|
||||
import Loading from "../common/Loading"
|
||||
import Error from "../common/Error"
|
||||
|
||||
const addXRankHelp = (
|
||||
<div>
|
||||
This user has no X Rank profile set up. If you are the owner of this user
|
||||
page here is how you can set it up:
|
||||
<div style={{ paddingTop: "5px" }}>
|
||||
<List ordered>
|
||||
<List.Item>
|
||||
Finish an X rank season in the Top 500 in at least one mode.
|
||||
</List.Item>
|
||||
<List.Item>Send your Twitter handle to Sendou via DM.</List.Item>
|
||||
<List.Item>
|
||||
Add your Twitter account to your profile on Discord, verify it and
|
||||
make sure it's set to appear publicly.
|
||||
</List.Item>
|
||||
<List.Item>Log in to sendou.ink</List.Item>
|
||||
</List>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const PlayerXRankStats = ({ twitter, tabMode = false }) => {
|
||||
let searchVariables = {}
|
||||
const { uid } = useParams()
|
||||
if (uid) searchVariables = { uid }
|
||||
if (twitter) searchVariables = { twitter }
|
||||
const { data, error, loading } = useQuery(playerInfo, {
|
||||
variables: searchVariables
|
||||
})
|
||||
const [top, setTop] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
return
|
||||
}
|
||||
if (data && data.playerInfo) {
|
||||
document.title = `${data.playerInfo.player.name} - X Rank - sendou.ink`
|
||||
const placements = data.playerInfo.placements
|
||||
|
||||
//reducing placements to top sz, tc etc. rank and x power
|
||||
const tops = ["", "szTop", "tcTop", "rmTop", "cbTop"]
|
||||
const exs = ["", "szX", "tcX", "rmX", "cbX"]
|
||||
setTop(
|
||||
placements.reduce(
|
||||
(acc, cur) => {
|
||||
const topKey = tops[cur.mode]
|
||||
const xKey = exs[cur.mode]
|
||||
if (!acc[xKey]) {
|
||||
acc[xKey] = cur
|
||||
acc[topKey] = cur
|
||||
return acc
|
||||
}
|
||||
if (acc[xKey].x_power < cur.x_power) {
|
||||
acc[xKey] = cur
|
||||
}
|
||||
if (acc[topKey].rank > cur.rank) {
|
||||
acc[topKey] = cur
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
{
|
||||
szX: null,
|
||||
szTop: null,
|
||||
tcX: null,
|
||||
tcTop: null,
|
||||
rmX: null,
|
||||
rmTop: null,
|
||||
cbX: null,
|
||||
cbTop: null
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}, [data, loading])
|
||||
|
||||
if (!uid && !twitter) return addXRankHelp
|
||||
if (loading) return <Loading />
|
||||
if (error) {
|
||||
if (error.message === "GraphQL error: player not found") {
|
||||
if (tabMode) return addXRankHelp
|
||||
else return <Redirect to="/404" />
|
||||
}
|
||||
return <Error errorMessage={error.message} />
|
||||
}
|
||||
const playerData = data.playerInfo.player
|
||||
return (
|
||||
<>
|
||||
{!tabMode ? (
|
||||
<Header as="h2">
|
||||
{playerData.twitter ? (
|
||||
<Image
|
||||
circular
|
||||
src={`https://avatars.io/twitter/${playerData.twitter}`}
|
||||
/>
|
||||
) : null}{" "}
|
||||
{playerData.alias ? playerData.alias : playerData.name}
|
||||
{playerData.twitter ? (
|
||||
<a href={`https://twitter.com/${playerData.twitter}`}>
|
||||
<Icon
|
||||
style={{ paddingLeft: "5px" }}
|
||||
size="small"
|
||||
name="twitter"
|
||||
/>
|
||||
</a>
|
||||
) : null}
|
||||
</Header>
|
||||
) : null}
|
||||
{tabMode && playerData.discord_id ? (
|
||||
<>
|
||||
<Link to={`/u/${playerData.discord_id}`}>User page</Link>
|
||||
<br />
|
||||
</>
|
||||
) : null}
|
||||
<Segment compact>
|
||||
<div style={{ padding: "5px" }}>
|
||||
<TopPlacementsTable top={top} />
|
||||
</div>
|
||||
</Segment>
|
||||
<Segment compact>
|
||||
<div style={{ padding: "5px" }}>
|
||||
<Header dividing>All Top 500 placements</Header>
|
||||
<MonthsTable placements={data.playerInfo.placements} />
|
||||
</div>
|
||||
</Segment>
|
||||
<Segment compact>
|
||||
<div style={{ padding: "5px" }}>
|
||||
<Header dividing>Weapons reached Top 500 with</Header>
|
||||
<WpnPlayedTable weapons={playerData.weapons} />
|
||||
</div>
|
||||
</Segment>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default PlayerXRankStats
|
||||
118
react-ui/src/components/xsearch/TopPlacementsTable.js
vendored
Normal file
118
react-ui/src/components/xsearch/TopPlacementsTable.js
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import React from "react"
|
||||
import { Header, Image, Table } from "semantic-ui-react"
|
||||
|
||||
import { wpnSmall } from "../../assets/imageImports"
|
||||
import weaponDict from "../../utils/english_internal.json"
|
||||
import { months } from "../../utils/lists"
|
||||
import szIcon from "../../assets/sz.png"
|
||||
import tcIcon from "../../assets/tc.png"
|
||||
import rmIcon from "../../assets/rm.png"
|
||||
import cbIcon from "../../assets/cb.png"
|
||||
|
||||
const TopPlacementsTable = ({ top }) => {
|
||||
const returnRow = (x, placement, mode) => {
|
||||
if (!x) {
|
||||
return null
|
||||
}
|
||||
|
||||
const modeIcon = ["", szIcon, tcIcon, rmIcon, cbIcon][mode]
|
||||
const modeName = [
|
||||
"",
|
||||
"Splat Zones",
|
||||
"Tower Control",
|
||||
"Rainmaker",
|
||||
"Clam Blitz"
|
||||
][mode]
|
||||
|
||||
if (x === placement) {
|
||||
return (
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Header as="h4" image>
|
||||
<Image src={modeIcon} size="mini" />
|
||||
<Header.Content>
|
||||
{modeName}
|
||||
<Header.Subheader>
|
||||
Highest X Power & Placement
|
||||
</Header.Subheader>
|
||||
</Header.Content>
|
||||
</Header>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{x.x_power}</Table.Cell>
|
||||
<Table.Cell>{x.rank}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<img src={wpnSmall[weaponDict[x.weapon]]} alt={x.weapon} />
|
||||
</Table.Cell>
|
||||
<Table.Cell>{months[x.month]}</Table.Cell>
|
||||
<Table.Cell>{x.year}</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Header as="h4" image>
|
||||
<Image src={modeIcon} size="mini" />
|
||||
<Header.Content>
|
||||
{modeName}
|
||||
<Header.Subheader>Highest X Power</Header.Subheader>
|
||||
</Header.Content>
|
||||
</Header>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{x.x_power}</Table.Cell>
|
||||
<Table.Cell>{x.rank}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<img src={wpnSmall[weaponDict[x.weapon]]} alt={x.weapon} />
|
||||
</Table.Cell>
|
||||
<Table.Cell>{months[x.month]}</Table.Cell>
|
||||
<Table.Cell>{x.year}</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Header as="h4" image>
|
||||
<Image src={modeIcon} size="mini" />
|
||||
<Header.Content>
|
||||
{modeName}
|
||||
<Header.Subheader>Highest Placement</Header.Subheader>
|
||||
</Header.Content>
|
||||
</Header>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{placement.x_power}</Table.Cell>
|
||||
<Table.Cell>{placement.rank}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<img
|
||||
src={wpnSmall[weaponDict[placement.weapon]]}
|
||||
alt={placement.weapon}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{months[placement.month]}</Table.Cell>
|
||||
<Table.Cell>{placement.year}</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Table basic="very" celled collapsing>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell></Table.HeaderCell>
|
||||
<Table.HeaderCell>X Power</Table.HeaderCell>
|
||||
<Table.HeaderCell>Placement</Table.HeaderCell>
|
||||
<Table.HeaderCell>Weapon</Table.HeaderCell>
|
||||
<Table.HeaderCell>Month</Table.HeaderCell>
|
||||
<Table.HeaderCell>Year</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
{returnRow(top.szX, top.szTop, 1)}
|
||||
{returnRow(top.tcX, top.tcTop, 2)}
|
||||
{returnRow(top.rmX, top.rmTop, 3)}
|
||||
{returnRow(top.cbX, top.cbTop, 4)}
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
export default TopPlacementsTable
|
||||
63
react-ui/src/components/xsearch/WpnPlayedTable.js
vendored
Normal file
63
react-ui/src/components/xsearch/WpnPlayedTable.js
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import React from "react"
|
||||
import { weaponsByCategory } from "../../utils/lists"
|
||||
import { categoryKeys } from "../../utils/lists"
|
||||
import weaponDict from "../../utils/english_internal.json"
|
||||
import { wpnSmall } from "../../assets/imageImports"
|
||||
import { Table, Header, Popup } from "semantic-ui-react"
|
||||
|
||||
const WpnPlayedTable = ({ weapons }) => {
|
||||
const weaponStyle = wpnName => {
|
||||
const activeStyle = {}
|
||||
const inactiveStyle = { filter: "grayscale(1)", opacity: "0.3" }
|
||||
|
||||
return weapons.includes(wpnName) ? activeStyle : inactiveStyle
|
||||
}
|
||||
return (
|
||||
<Table basic="very" celled collapsing>
|
||||
{categoryKeys.map(c => {
|
||||
return (
|
||||
<Table.Body key={c}>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Header as="h4">
|
||||
<Header.Content>{c}</Header.Content>
|
||||
</Header>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{weaponsByCategory[c].map(w => {
|
||||
return (
|
||||
<Popup
|
||||
key={w}
|
||||
trigger={
|
||||
<img
|
||||
key={w}
|
||||
style={weaponStyle(w)}
|
||||
src={wpnSmall[weaponDict[w]]}
|
||||
alt={w}
|
||||
/>
|
||||
}
|
||||
content={w}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
)
|
||||
})}
|
||||
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Header as="h4">
|
||||
<Header.Content>Total</Header.Content>
|
||||
</Header>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{weapons.length} / 129</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
export default WpnPlayedTable
|
||||
350
react-ui/src/components/xtrends/XTrends.js
vendored
Normal file
350
react-ui/src/components/xtrends/XTrends.js
vendored
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
import React, { useState, useEffect } from "react"
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
Radio,
|
||||
List,
|
||||
Image,
|
||||
Grid,
|
||||
Dropdown,
|
||||
Message
|
||||
} from "semantic-ui-react"
|
||||
import { wpnSmall } from "../../assets/imageImports"
|
||||
import useTrends from "../../utils/useTrends"
|
||||
import useWindowDimensions from "../../utils/useWindowDimensions"
|
||||
import english_internal from "../../utils/english_internal.json"
|
||||
import { months } from "../../utils/lists"
|
||||
import WeaponDropdown from "../common/WeaponDropdown"
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
Brush
|
||||
} from "recharts"
|
||||
|
||||
const customToolTipStyle = {
|
||||
width: "200px",
|
||||
margin: "0",
|
||||
lineHeight: "24px",
|
||||
border: "1px solid #f5f5f5",
|
||||
backgroundColor: "hsla(0,0%,100%,.8)",
|
||||
padding: "10px"
|
||||
}
|
||||
|
||||
const labelStyle = {
|
||||
margin: "0",
|
||||
color: "#666",
|
||||
fontWeight: "700"
|
||||
}
|
||||
|
||||
const introStyle = {
|
||||
borderTop: "1px solid #f5f5f5",
|
||||
margin: "0"
|
||||
}
|
||||
|
||||
const descStyle = {
|
||||
margin: 0,
|
||||
color: "#999"
|
||||
}
|
||||
|
||||
const XTrends = () => {
|
||||
const [weaponForm, setWeaponForm] = useState(null)
|
||||
const [combineFormLeft, setCombineFormLeft] = useState(null)
|
||||
const [combineFormRight, setCombineFormRight] = useState(null)
|
||||
const [weaponForDispatch, setWeaponForDispatch] = useState(null)
|
||||
const [mode, setMode] = useState("ALL")
|
||||
const [modeForDispatch, setModeForDispatch] = useState(null)
|
||||
const { loading, error, plotData, dispatch } = useTrends(
|
||||
weaponForDispatch,
|
||||
modeForDispatch
|
||||
)
|
||||
const { containerWidth } = useWindowDimensions()
|
||||
|
||||
useEffect(() => {
|
||||
document.title = "X Rank Trends - sendou.ink"
|
||||
}, [])
|
||||
|
||||
const CustomTooltip = ({ active, payload }) => {
|
||||
if (active) {
|
||||
if (payload.length === 0) return null
|
||||
const monthNumber = payload[0].payload.name
|
||||
const yearNumber = payload[0].payload.year
|
||||
let patchDescription = null
|
||||
if (payload[0].payload.hasOwnProperty("patch")) {
|
||||
if (monthNumber === 4 && yearNumber === 2019) {
|
||||
patchDescription = "Patches 4.6 and 4.7 were released."
|
||||
} else {
|
||||
patchDescription = `Patch ${payload[0].payload.patch} was released.`
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div style={customToolTipStyle}>
|
||||
<p style={labelStyle}>{`${months[monthNumber]} (${yearNumber})`}</p>
|
||||
{payload.map(p => {
|
||||
return (
|
||||
<p style={{ ...introStyle, color: p.stroke }} key={p.dataKey}>
|
||||
{p.payload[p.dataKey]} - {p.dataKey}
|
||||
</p>
|
||||
)
|
||||
})}
|
||||
{patchDescription && <p style={descStyle}>{patchDescription}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div style={{ color: "red" }}>{error.message}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WeaponDropdown
|
||||
value={weaponForm}
|
||||
onChange={(e, { value }) => setWeaponForm(value)}
|
||||
/>
|
||||
<div style={{ paddingTop: "10px" }}>
|
||||
<Form>
|
||||
<Form.Field>
|
||||
<Radio
|
||||
label="All Modes"
|
||||
name="radioGroup"
|
||||
value="ALL"
|
||||
checked={mode === "ALL"}
|
||||
onChange={() => setMode("ALL")}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<Radio
|
||||
label="Splat Zones"
|
||||
name="radioGroup"
|
||||
value="SZ"
|
||||
checked={mode === "SZ"}
|
||||
onChange={() => setMode("SZ")}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<Radio
|
||||
label="Tower Control"
|
||||
name="radioGroup"
|
||||
value="TC"
|
||||
checked={mode === "TC"}
|
||||
onChange={() => setMode("TC")}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<Radio
|
||||
label="Rainmaker"
|
||||
name="radioGroup"
|
||||
value="RM"
|
||||
checked={mode === "RM"}
|
||||
onChange={() => setMode("RM")}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<Radio
|
||||
label="Clam Blitz"
|
||||
name="radioGroup"
|
||||
value="CB"
|
||||
checked={mode === "CB"}
|
||||
onChange={() => setMode("CB")}
|
||||
/>
|
||||
</Form.Field>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ paddingTop: "10px" }}>
|
||||
<Button
|
||||
loading={loading}
|
||||
onClick={() => {
|
||||
setWeaponForDispatch(weaponForm)
|
||||
setModeForDispatch(mode)
|
||||
const wpnForCombine =
|
||||
mode === "ALL" ? weaponForm : `${weaponForm} (${mode})`
|
||||
setCombineFormLeft(wpnForCombine)
|
||||
if (plotData.keys.length >= 1)
|
||||
setCombineFormRight(
|
||||
plotData.keys[plotData.keys.length - 1].weapon
|
||||
)
|
||||
}}
|
||||
disabled={!weaponForm}
|
||||
>
|
||||
Add to plot as new
|
||||
</Button>
|
||||
{plotData.keys.length >= 2 && (
|
||||
<div style={{ paddingTop: "10px" }}>
|
||||
<Button
|
||||
disabled={
|
||||
!combineFormLeft ||
|
||||
!combineFormRight ||
|
||||
combineFormLeft === combineFormRight
|
||||
}
|
||||
onClick={() => {
|
||||
dispatch({
|
||||
type: "combine",
|
||||
left: combineFormLeft,
|
||||
right: combineFormRight
|
||||
})
|
||||
}}
|
||||
>
|
||||
Combine...
|
||||
</Button>
|
||||
<span>
|
||||
{" "}
|
||||
<Dropdown
|
||||
inline
|
||||
options={plotData.keys
|
||||
.map(k => {
|
||||
return { text: k.weapon, value: k.weapon }
|
||||
})
|
||||
.filter(k => k.text !== combineFormRight)}
|
||||
onChange={(event, { value }) => {
|
||||
setCombineFormLeft(value)
|
||||
}}
|
||||
value={combineFormLeft}
|
||||
/>{" "}
|
||||
with{" "}
|
||||
<Dropdown
|
||||
inline
|
||||
options={plotData.keys
|
||||
.map(k => {
|
||||
return { text: k.weapon, value: k.weapon }
|
||||
})
|
||||
.filter(k => k.text !== combineFormLeft)}
|
||||
onChange={(event, { value }) => {
|
||||
setCombineFormRight(value)
|
||||
}}
|
||||
value={combineFormRight}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{plotData.keys.length > 0 && (
|
||||
<div style={{ paddingTop: "10px" }}>
|
||||
<LineChart
|
||||
width={containerWidth}
|
||||
height={500}
|
||||
data={plotData.data}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 50,
|
||||
left: 0,
|
||||
bottom: 5
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="xLabel" tickLine={false} />
|
||||
<XAxis
|
||||
dataKey="patch"
|
||||
tickLine={true}
|
||||
scale="band"
|
||||
axisLine={false}
|
||||
height={1}
|
||||
xAxisId="patch"
|
||||
/>
|
||||
<YAxis
|
||||
allowDecimals={false}
|
||||
label={{
|
||||
value: "X rank top 500 placements",
|
||||
angle: -90,
|
||||
position: "insideLeft",
|
||||
textAnchor: "middle"
|
||||
}}
|
||||
/>
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
<Legend verticalAlign="top" />
|
||||
<Brush dataKey="xLabel" height={75} stroke="#000000" />
|
||||
{plotData.keys.map(keyObj => {
|
||||
const w = keyObj.weapon
|
||||
const color = keyObj.color
|
||||
return <Line key={w} type="monotone" dataKey={w} stroke={color} />
|
||||
})}
|
||||
</LineChart>
|
||||
<Grid columns={2} style={{ paddingTop: "10px" }}>
|
||||
<Grid.Column>
|
||||
<List divided verticalAlign="middle">
|
||||
{plotData.keys.map(keyObj => {
|
||||
const w = keyObj.weapon
|
||||
const color = keyObj.color
|
||||
return (
|
||||
<List.Item key={w}>
|
||||
<List.Content floated="right">
|
||||
<Button
|
||||
circular
|
||||
size="mini"
|
||||
negative
|
||||
icon="trash"
|
||||
onClick={() =>
|
||||
dispatch({ type: "delete", weapon: w })
|
||||
}
|
||||
/>
|
||||
</List.Content>
|
||||
{w.indexOf("+") === -1 && (
|
||||
<Image
|
||||
avatar
|
||||
src={
|
||||
wpnSmall[
|
||||
english_internal[
|
||||
w
|
||||
.replace(" (SZ)", "")
|
||||
.replace(" (TC)", "")
|
||||
.replace(" (RM)", "")
|
||||
.replace(" (CB)", "")
|
||||
]
|
||||
]
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<List.Content>
|
||||
<span
|
||||
style={{
|
||||
color: color,
|
||||
background: "none",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
WebkitUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
MsUserSelect: "none",
|
||||
userSelect: "none"
|
||||
}}
|
||||
onClick={() =>
|
||||
dispatch({ type: "randomizeColor", weapon: w })
|
||||
}
|
||||
>
|
||||
{w}
|
||||
</span>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Grid.Column>
|
||||
<Grid.Column />
|
||||
</Grid>
|
||||
</div>
|
||||
)}
|
||||
<Message>
|
||||
<Message.List>
|
||||
<Message.Item>
|
||||
You can check out all the patch notes{" "}
|
||||
<a href="https://splatoonwiki.org/wiki/List_of_updates_in_Splatoon_2">
|
||||
here
|
||||
</a>
|
||||
</Message.Item>
|
||||
<Message.Item>
|
||||
For alternative take on X Rank trends check out{" "}
|
||||
<a href="https://www.splatmeta.ink">Splat Meta</a>
|
||||
</Message.Item>
|
||||
</Message.List>
|
||||
</Message>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default XTrends
|
||||
|
|
@ -6,7 +6,7 @@ body {
|
|||
height: 100%;
|
||||
margin: 0;
|
||||
background: #79f1a4;
|
||||
background-image: linear-gradient(135deg, #79f1a4 10%, #0e5cad 100%);
|
||||
background: linear-gradient(to right, #0f2027, #203a43, #2c5364);
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
|
|
|||
187
react-ui/src/utils/lists.js
vendored
187
react-ui/src/utils/lists.js
vendored
|
|
@ -154,6 +154,191 @@ export const weapons = [
|
|||
"Kensa Undercover Brella"
|
||||
]
|
||||
|
||||
export const shooters = [
|
||||
"Sploosh-o-matic",
|
||||
"Neo Sploosh-o-matic",
|
||||
"Sploosh-o-matic 7",
|
||||
"Splattershot Jr.",
|
||||
"Custom Splattershot Jr.",
|
||||
"Kensa Splattershot Jr.",
|
||||
"Splash-o-matic",
|
||||
"Neo Splash-o-matic",
|
||||
"Aerospray MG",
|
||||
"Aerospray RG",
|
||||
"Aerospray PG",
|
||||
"Splattershot",
|
||||
"Tentatek Splattershot",
|
||||
"Kensa Splattershot",
|
||||
".52 Gal",
|
||||
".52 Gal Deco",
|
||||
"Kensa .52 Gal",
|
||||
"N-ZAP '85",
|
||||
"N-ZAP '89",
|
||||
"N-ZAP '83",
|
||||
"Splattershot Pro",
|
||||
"Forge Splattershot Pro",
|
||||
"Kensa Splattershot Pro",
|
||||
".96 Gal",
|
||||
".96 Gal Deco",
|
||||
"Jet Squelcher",
|
||||
"Custom Jet Squelcher"
|
||||
]
|
||||
|
||||
export const semiauto = [
|
||||
"L-3 Nozzlenose",
|
||||
"L-3 Nozzlenose D",
|
||||
"Kensa L-3 Nozzlenose",
|
||||
"H-3 Nozzlenose",
|
||||
"H-3 Nozzlenose D",
|
||||
"Cherry H-3 Nozzlenose",
|
||||
"Squeezer",
|
||||
"Foil Squeezer"
|
||||
]
|
||||
|
||||
export const blasters = [
|
||||
"Luna Blaster",
|
||||
"Luna Blaster Neo",
|
||||
"Kensa Luna Blaster",
|
||||
"Blaster",
|
||||
"Custom Blaster",
|
||||
"Range Blaster",
|
||||
"Custom Range Blaster",
|
||||
"Grim Range Blaster",
|
||||
"Rapid Blaster",
|
||||
"Rapid Blaster Deco",
|
||||
"Kensa Rapid Blaster",
|
||||
"Rapid Blaster Pro",
|
||||
"Rapid Blaster Pro Deco",
|
||||
"Clash Blaster",
|
||||
"Clash Blaster Neo"
|
||||
]
|
||||
|
||||
export const rollers = [
|
||||
"Carbon Roller",
|
||||
"Carbon Roller Deco",
|
||||
"Splat Roller",
|
||||
"Krak-On Splat Roller",
|
||||
"Kensa Splat Roller",
|
||||
"Dynamo Roller",
|
||||
"Gold Dynamo Roller",
|
||||
"Kensa Dynamo Roller",
|
||||
"Flingza Roller",
|
||||
"Foil Flingza Roller"
|
||||
]
|
||||
|
||||
export const brushes = [
|
||||
"Inkbrush",
|
||||
"Inkbrush Nouveau",
|
||||
"Permanent Inkbrush",
|
||||
"Octobrush",
|
||||
"Octobrush Nouveau",
|
||||
"Kensa Octobrush"
|
||||
]
|
||||
|
||||
export const chargers = [
|
||||
"Classic Squiffer",
|
||||
"New Squiffer",
|
||||
"Fresh Squiffer",
|
||||
"Splat Charger",
|
||||
"Firefin Splat Charger",
|
||||
"Kensa Charger",
|
||||
"Splatterscope",
|
||||
"Firefin Splatterscope",
|
||||
"Kensa Splatterscope",
|
||||
"E-liter 4K",
|
||||
"Custom E-liter 4K",
|
||||
"E-liter 4K Scope",
|
||||
"Custom E-liter 4K Scope",
|
||||
"Bamboozler 14 Mk I",
|
||||
"Bamboozler 14 Mk II",
|
||||
"Bamboozler 14 Mk III",
|
||||
"Goo Tuber",
|
||||
"Custom Goo Tuber"
|
||||
]
|
||||
|
||||
export const sloshers = [
|
||||
"Slosher",
|
||||
"Slosher Deco",
|
||||
"Soda Slosher",
|
||||
"Tri-Slosher",
|
||||
"Tri-Slosher Nouveau",
|
||||
"Sloshing Machine",
|
||||
"Sloshing Machine Neo",
|
||||
"Kensa Sloshing Machine",
|
||||
"Bloblobber",
|
||||
"Bloblobber Deco",
|
||||
"Explosher",
|
||||
"Custom Explosher"
|
||||
]
|
||||
|
||||
export const splatlings = [
|
||||
"Mini Splatling",
|
||||
"Zink Mini Splatling",
|
||||
"Kensa Mini Splatling",
|
||||
"Heavy Splatling",
|
||||
"Heavy Splatling Deco",
|
||||
"Heavy Splatling Remix",
|
||||
"Hydra Splatling",
|
||||
"Custom Hydra Splatling",
|
||||
"Ballpoint Splatling",
|
||||
"Ballpoint Splatling Nouveau",
|
||||
"Nautilus 47",
|
||||
"Nautilus 79"
|
||||
]
|
||||
|
||||
export const dualies = [
|
||||
"Dapple Dualies",
|
||||
"Dapple Dualies Nouveau",
|
||||
"Clear Dapple Dualies",
|
||||
"Splat Dualies",
|
||||
"Enperry Splat Dualies",
|
||||
"Kensa Splat Dualies",
|
||||
"Glooga Dualies",
|
||||
"Glooga Dualies Deco",
|
||||
"Kensa Glooga Dualies",
|
||||
"Dualie Squelchers",
|
||||
"Custom Dualie Squelchers",
|
||||
"Dark Tetra Dualies",
|
||||
"Light Tetra Dualies"
|
||||
]
|
||||
|
||||
export const brellas = [
|
||||
"Splat Brella",
|
||||
"Sorella Brella",
|
||||
"Tenta Brella",
|
||||
"Tenta Sorella Brella",
|
||||
"Tenta Camo Brella",
|
||||
"Undercover Brella",
|
||||
"Undercover Sorella Brella",
|
||||
"Kensa Undercover Brella"
|
||||
]
|
||||
|
||||
export const weaponsByCategory = {
|
||||
Shooters: shooters,
|
||||
"Semi-automatic Shooters": semiauto,
|
||||
Blasters: blasters,
|
||||
Rollers: rollers,
|
||||
Brushes: brushes,
|
||||
Chargers: chargers,
|
||||
Sloshers: sloshers,
|
||||
Splatlings: splatlings,
|
||||
Dualies: dualies,
|
||||
Brellas: brellas
|
||||
}
|
||||
|
||||
export const categoryKeys = [
|
||||
"Shooters",
|
||||
"Semi-automatic Shooters",
|
||||
"Blasters",
|
||||
"Rollers",
|
||||
"Brushes",
|
||||
"Chargers",
|
||||
"Sloshers",
|
||||
"Splatlings",
|
||||
"Dualies",
|
||||
"Brellas"
|
||||
]
|
||||
|
||||
export const clothingGear = [
|
||||
"Clt_AMB000",
|
||||
"Clt_AMB001",
|
||||
|
|
@ -442,7 +627,6 @@ export const headGear = [
|
|||
"Hed_CAP011",
|
||||
"Hed_CAP012",
|
||||
"Hed_CAP014",
|
||||
"Hed_CAP015",
|
||||
"Hed_CAP018",
|
||||
"Hed_CAP019",
|
||||
"Hed_CAP020",
|
||||
|
|
@ -569,7 +753,6 @@ export const headGear = [
|
|||
"Hed_NCP004",
|
||||
"Hed_NCP005",
|
||||
"Hed_NCP006",
|
||||
"Hed_NCP007",
|
||||
"Hed_NCP008",
|
||||
"Hed_NCP009",
|
||||
"Hed_NCP010",
|
||||
|
|
|
|||
278
react-ui/src/utils/useTrends.js
vendored
Normal file
278
react-ui/src/utils/useTrends.js
vendored
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
import { useReducer, useEffect } from "react"
|
||||
import { useQuery } from "@apollo/react-hooks"
|
||||
import { searchForTrend } from "../graphql/queries/seachForTrend"
|
||||
|
||||
const month = []
|
||||
month[0] = null
|
||||
month[1] = "Jan"
|
||||
month[2] = "Feb"
|
||||
month[3] = "Mar"
|
||||
month[4] = "Apr"
|
||||
month[5] = "May"
|
||||
month[6] = "Jun"
|
||||
month[7] = "Jul"
|
||||
month[8] = "Aug"
|
||||
month[9] = "Sep"
|
||||
month[10] = "Oct"
|
||||
month[11] = "Nov"
|
||||
month[12] = "Dec"
|
||||
|
||||
const patches = {
|
||||
5: {
|
||||
2018: {
|
||||
name: "3.0",
|
||||
link: "https://splatoonwiki.org/wiki/Version_3.0.0_(Splatoon_2)"
|
||||
},
|
||||
2019: {
|
||||
name: "4.8",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.8.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
6: {
|
||||
2018: {
|
||||
name: "3.1",
|
||||
link: "https://splatoonwiki.org/wiki/Version_3.1.0_(Splatoon_2)"
|
||||
},
|
||||
2019: {
|
||||
name: "4.9",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.9.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
7: {
|
||||
2018: {
|
||||
name: "3.2",
|
||||
link: "https://splatoonwiki.org/wiki/Version_3.2.0_(Splatoon_2)"
|
||||
},
|
||||
2019: {
|
||||
name: "5.0",
|
||||
link: "https://splatoonwiki.org/wiki/Version_5.0.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
9: {
|
||||
2018: {
|
||||
name: "4.0",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.0.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
10: {
|
||||
2018: {
|
||||
name: "4.1",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.1.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
11: {
|
||||
2018: {
|
||||
name: "4.2",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.2.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
12: {
|
||||
2018: {
|
||||
name: "4.3",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.3.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
1: {
|
||||
2019: {
|
||||
name: "4.4",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.4.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
3: {
|
||||
2019: {
|
||||
name: "4.5",
|
||||
link: "https://splatoonwiki.org/wiki/Version_4.5.0_(Splatoon_2)"
|
||||
}
|
||||
},
|
||||
4: {
|
||||
2019: {
|
||||
name: "4.6+4.7",
|
||||
link: "https://splatoonwiki.org/wiki/List_of_updates_in_Splatoon_2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const presetColors = [
|
||||
"#FF00FF",
|
||||
"#008000",
|
||||
"#FF0000",
|
||||
"#0000FF",
|
||||
"#FFA500",
|
||||
"#800080",
|
||||
"#A52A2A",
|
||||
"#1BC5CD",
|
||||
"#000080",
|
||||
"#5BCCA0"
|
||||
]
|
||||
|
||||
const setPlotDataInitial = () => {
|
||||
const arr_to_return = []
|
||||
for (let i = 5; i < 13; i++) {
|
||||
if (patches.hasOwnProperty(i) && patches[i].hasOwnProperty(2018)) {
|
||||
arr_to_return.push({
|
||||
name: i,
|
||||
year: 2018,
|
||||
xLabel: month[i],
|
||||
patch: patches[i][2018].name
|
||||
})
|
||||
} else {
|
||||
arr_to_return.push({ name: i, year: 2018, xLabel: month[i] })
|
||||
}
|
||||
}
|
||||
const d = new Date()
|
||||
const year = d.getFullYear()
|
||||
const currentMonth = d.getMonth() + 1
|
||||
for (let i = 2019; i < year + 1; i++) {
|
||||
for (let j = 1; j < 13; j++) {
|
||||
// break the loop when we reach the future
|
||||
if (i === year && j === currentMonth) break
|
||||
const xLabel = j === 1 ? `Jan (${year})` : month[j]
|
||||
if (patches.hasOwnProperty(j) && patches[j].hasOwnProperty(i)) {
|
||||
arr_to_return.push({
|
||||
name: j,
|
||||
year: i,
|
||||
xLabel,
|
||||
patch: patches[j][i].name
|
||||
})
|
||||
} else {
|
||||
arr_to_return.push({ name: j, year: i, xLabel })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr_to_return
|
||||
}
|
||||
|
||||
const mergeModeArrays = countObj => {
|
||||
const sz_arr = countObj["SZ"]
|
||||
const tc_arr = countObj["TC"]
|
||||
const rm_arr = countObj["RM"]
|
||||
const cb_arr = countObj["CB"]
|
||||
const arr_to_return = new Array(12).fill(0)
|
||||
for (let i = 1; i < 13; i++) {
|
||||
arr_to_return[i] = sz_arr[i] + tc_arr[i] + rm_arr[i] + cb_arr[i]
|
||||
}
|
||||
return arr_to_return
|
||||
}
|
||||
|
||||
// monthIndex is between 1 and 12 (inclusive)
|
||||
const resolveStartIndex = (monthIndex, year) => {
|
||||
// resolves start index for an array where index 0 is always May 2018
|
||||
if (year === 2018) {
|
||||
return monthIndex - 5
|
||||
} else if (year === 2019) {
|
||||
return 7 + monthIndex
|
||||
} else {
|
||||
return 7 + monthIndex + (year - 2019) * 12
|
||||
}
|
||||
}
|
||||
|
||||
const getColor = state => {
|
||||
if (state.keys.length < 9) {
|
||||
return presetColors[state.keys.length]
|
||||
}
|
||||
|
||||
return "#000000".replace(/0/g, function() {
|
||||
return (~~(Math.random() * 16)).toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "add":
|
||||
const trend = action.trendDocument
|
||||
const mode = action.mode
|
||||
const modeLabel = mode === "ALL" ? "" : ` (${mode})`
|
||||
const weapon = `${action.trendDocument.weapon}${modeLabel}`
|
||||
// don't add duplicate plots
|
||||
if (state.keys.indexOf(weapon) !== -1) return state
|
||||
const toPlotData = [...state.data]
|
||||
for (let index = 0; index < trend.counts.length; index++) {
|
||||
let year = trend.counts[index].year
|
||||
let year_arr = null
|
||||
if (mode === "ALL") {
|
||||
year_arr = mergeModeArrays(trend.counts[index])
|
||||
} else {
|
||||
year_arr = trend.counts[index][mode]
|
||||
}
|
||||
|
||||
for (let i = 1; i < 13; i++) {
|
||||
// if year is 2018 skipping to the index where values are found
|
||||
if (year === 2018 && i < 5) {
|
||||
i = 4
|
||||
continue
|
||||
}
|
||||
const plotIndex = resolveStartIndex(i, year)
|
||||
if (plotIndex === toPlotData.length) break
|
||||
toPlotData[plotIndex][weapon] = year_arr[i]
|
||||
}
|
||||
}
|
||||
|
||||
const keyObj = {
|
||||
weapon,
|
||||
color: getColor(state)
|
||||
}
|
||||
return { data: toPlotData, keys: [...state.keys, keyObj] }
|
||||
case "delete":
|
||||
const weaponToDelete = action.weapon
|
||||
const newKeys = [...state.keys].filter(k => k.weapon !== weaponToDelete)
|
||||
return { data: state.data, keys: newKeys }
|
||||
case "combine":
|
||||
const weaponLeft = action.left
|
||||
const weaponRight = action.right
|
||||
const newKey = `${weaponLeft} + ${weaponRight}`
|
||||
const newKeysWithCombined = [
|
||||
...state.keys,
|
||||
{ weapon: newKey, color: getColor(state) }
|
||||
].filter(k => k.weapon !== weaponLeft && k.weapon !== weaponRight)
|
||||
const newDataWithCombined = [...state.data].map(d => {
|
||||
const dataObj = { ...d }
|
||||
let weaponLeftCount = 0
|
||||
let weaponRightCount = 0
|
||||
if (dataObj.hasOwnProperty(weaponLeft))
|
||||
weaponLeftCount = dataObj[weaponLeft]
|
||||
if (dataObj.hasOwnProperty(weaponRight))
|
||||
weaponRightCount = dataObj[weaponRight]
|
||||
dataObj[newKey] = weaponLeftCount + weaponRightCount
|
||||
delete dataObj[weaponLeft]
|
||||
delete dataObj[weaponRight]
|
||||
return dataObj
|
||||
})
|
||||
|
||||
return { data: newDataWithCombined, keys: newKeysWithCombined }
|
||||
case "randomizeColor":
|
||||
const keysWithNewColor = [...state.keys].map(k => {
|
||||
if (k.weapon !== action.weapon) {
|
||||
return k
|
||||
}
|
||||
return {
|
||||
...k,
|
||||
color: "#000000".replace(/0/g, function() {
|
||||
return (~~(Math.random() * 16)).toString(16)
|
||||
})
|
||||
}
|
||||
})
|
||||
return { data: state.data, keys: keysWithNewColor }
|
||||
default:
|
||||
throw new Error()
|
||||
}
|
||||
}
|
||||
|
||||
export default function useTrends(weapon, mode) {
|
||||
const [plotData, dispatch] = useReducer(reducer, {
|
||||
data: setPlotDataInitial(),
|
||||
keys: []
|
||||
})
|
||||
// Skip query if there is no weapon provided
|
||||
const { data, loading, error } = useQuery(searchForTrend, {
|
||||
skip: !weapon,
|
||||
variables: { weapon }
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || !data) return
|
||||
dispatch({ type: "add", trendDocument: data.searchForTrend, mode })
|
||||
}, [loading, data, mode])
|
||||
|
||||
return { loading, error, plotData, dispatch }
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user