From 5230d1dc77b59d4ccd7149e8cd391d188bc2cc03 Mon Sep 17 00:00:00 2001 From: Sendou Date: Mon, 13 Apr 2020 01:40:19 +0300 Subject: [PATCH] x trends done --- frontend-react/package-lock.json | 221 ++++++++++++++++++ frontend-react/package.json | 2 + .../src/components/elements/Alert.tsx | 2 +- .../src/components/root/SideNavContent.tsx | 6 + .../components/xtrends/WeaponLineChart.tsx | 112 +++++++++ .../src/components/xtrends/XTrendsPage.tsx | 94 +++++--- 6 files changed, 399 insertions(+), 38 deletions(-) create mode 100644 frontend-react/src/components/xtrends/WeaponLineChart.tsx diff --git a/frontend-react/package-lock.json b/frontend-react/package-lock.json index 7d5164f4f..9461a2c4e 100644 --- a/frontend-react/package-lock.json +++ b/frontend-react/package-lock.json @@ -2199,6 +2199,19 @@ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, + "@types/d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==" + }, + "@types/d3-shape": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.2.tgz", + "integrity": "sha512-LtD8EaNYCaBRzHzaAiIPrfcL3DdIysc81dkGlQvv7WQP3+YXV7b0JJTtR1U3bzeRieS603KF4wUo+ZkJVenh8w==", + "requires": { + "@types/d3-path": "*" + } + }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -2460,6 +2473,21 @@ "@types/react": "*" } }, + "@types/recharts": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.9.tgz", + "integrity": "sha512-J4sZYDfdbFf1aLzenOksdd2s8D/GWh8tftgaE3oMDkvgfOwba0d8DxP0hnE2+WzsSy4fSvHWqrKZv5r2hVH47A==", + "requires": { + "@types/d3-shape": "*", + "@types/react": "*", + "@types/recharts-scale": "*" + } + }, + "@types/recharts-scale": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/recharts-scale/-/recharts-scale-1.0.0.tgz", + "integrity": "sha512-HR/PrCcxYb2YHviTqH7CMdL1TUhUZLTUKzfrkMhxm1HTa5mg/QtP8XMiuSPz6dZ6wecazAOu8aYZ5DqkNlgHHQ==" + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -5204,6 +5232,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.4", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.4.tgz", + "integrity": "sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw==" + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "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.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "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.2.3", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.2.3.tgz", + "integrity": "sha512-RAHNnD8+XvC4Zc4d2A56Uw0yJoM7bsvOlJR33bclxq399Rak/b9bhvu/InjxdWhPtkgU53JJcleJTGkNRnN6IA==", + "requires": { + "d3-time": "1" + } + }, "damerau-levenshtein": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", @@ -5257,6 +5352,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", @@ -9631,6 +9731,11 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, + "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", @@ -9658,6 +9763,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.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -9778,6 +9888,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.22", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz", + "integrity": "sha512-L0j0tFVZBQQLeEjmWOvDLoRciIY8gQGWahvkztXUal8jH8R5Rlqo9GCvgqvXcy9LQhEWdQCVvzqAbxgYNt4blQ==" + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -12790,6 +12905,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-scripts": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.1.tgz", @@ -12875,6 +13001,38 @@ "react-transition-group": "^4.3.0" } }, + "react-smooth": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-1.0.5.tgz", + "integrity": "sha512-eW057HT0lFgCKh8ilr0y2JaH2YbNcuEdFpxyg7Gf/qDKk9hqGMyXryZJ8iMGJEuKH0+wxS0ccSsBBB3W8yCn8w==", + "requires": { + "lodash": "~4.17.4", + "prop-types": "^15.6.0", + "raf": "^3.4.0", + "react-transition-group": "^2.5.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + } + } + }, "react-spring": { "version": "8.0.27", "resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz", @@ -12966,6 +13124,39 @@ "util.promisify": "^1.0.0" } }, + "recharts": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-1.8.5.tgz", + "integrity": "sha512-tM9mprJbXVEBxjM7zHsIy6Cc41oO/pVYqyAsOHLxlJrbNBuLs0PHB3iys2M+RqCF0//k8nJtZF6X6swSkWY3tg==", + "requires": { + "classnames": "^2.2.5", + "core-js": "^2.6.10", + "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.5", + "recharts-scale": "^0.4.2", + "reduce-css-calc": "^1.3.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + } + } + }, + "recharts-scale": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.3.tgz", + "integrity": "sha512-t8p5sccG9Blm7c1JQK/ak9O8o95WGhNXD7TXg/BW5bYbVlr6eCeRBNpgyigD4p6pSSMehC5nSvBUPj6F68rbFA==", + "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", @@ -12983,6 +13174,31 @@ "strip-indent": "^3.0.0" } }, + "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", @@ -13269,6 +13485,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.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.2.tgz", diff --git a/frontend-react/package.json b/frontend-react/package.json index c1967e42c..6bf0029e0 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -22,6 +22,7 @@ "@types/react-infinite-scroller": "^1.2.1", "@types/react-paginate": "^6.2.1", "@types/react-select": "^3.0.11", + "@types/recharts": "^1.8.9", "@types/webpack-env": "^1.15.1", "apollo-boost": "^0.4.7", "emotion-theming": "^10.0.27", @@ -44,6 +45,7 @@ "react-string-replace": "^0.4.4", "react-super-responsive-table": "^5.1.1", "react-tweet-embed": "^1.2.2", + "recharts": "^1.8.5", "source-map-explorer": "^2.4.2", "typescript": "^3.8.3", "use-query-params": "^0.6.0" diff --git a/frontend-react/src/components/elements/Alert.tsx b/frontend-react/src/components/elements/Alert.tsx index e1e2dfe81..7b2d5435d 100644 --- a/frontend-react/src/components/elements/Alert.tsx +++ b/frontend-react/src/components/elements/Alert.tsx @@ -2,7 +2,7 @@ import React from "react" import { Alert as ChakraAlert, AlertIcon, CloseButton } from "@chakra-ui/core" interface AlertProps { - children: string | string[] + children: string | string[] | JSX.Element | JSX.Element[] status: "error" | "success" | "warning" | "info" mt?: string onClose?: () => void diff --git a/frontend-react/src/components/root/SideNavContent.tsx b/frontend-react/src/components/root/SideNavContent.tsx index de99c7b09..2810937c3 100644 --- a/frontend-react/src/components/root/SideNavContent.tsx +++ b/frontend-react/src/components/root/SideNavContent.tsx @@ -37,6 +37,7 @@ import { UserData } from "../../types" import ColorPicker from "./ColorPicker" import Error from "../common/Error" import MyThemeContext from "../../themeContext" +import { MdTimeline } from "react-icons/md" const UserItem: React.FC<{ data?: UserData }> = ({ data }) => { const { themeColorWithShade, bgColor } = useContext(MyThemeContext) @@ -151,6 +152,11 @@ export const SideNavContent: React.FC = ({ showLogo = true }) => { /> + {data?.user?.plus?.membership_status && ( )} diff --git a/frontend-react/src/components/xtrends/WeaponLineChart.tsx b/frontend-react/src/components/xtrends/WeaponLineChart.tsx new file mode 100644 index 000000000..8dae7ec28 --- /dev/null +++ b/frontend-react/src/components/xtrends/WeaponLineChart.tsx @@ -0,0 +1,112 @@ +import React, { useContext } from "react" +import { LineChart, Line, Tooltip } from "recharts" +import MyThemeContext from "../../themeContext" +import { months } from "../../utils/lists" +import { Box, Flex, Badge } from "@chakra-ui/core" + +interface WeaponLineChartProps { + counts: { + year: number + SZ: (null | number)[] + TC: (null | number)[] + RM: (null | number)[] + CB: (null | number)[] + }[] + mode: "SZ" | "TC" | "RM" | "CB" + lastMonthYear: string +} + +interface CustomTooltipProps { + active?: any + payload?: any + label?: any +} + +const WeaponLineChart: React.FC = ({ + counts, + mode, + lastMonthYear, +}) => { + const { themeColor, themeColorHex, darkerBgColor } = useContext( + MyThemeContext + ) + + const lastMonthYearParts = lastMonthYear.split(" ") + const lastMonthIndex = months.indexOf(lastMonthYearParts[0] as any) + const lastYear = parseInt(lastMonthYearParts[1]) + + const getChartData = () => { + const countsPutTogether = counts.reduce((acc: number[], cur) => { + // in 2018 first X Rank month was May + if (cur.year === 2018) return [...acc, ...cur[mode].slice(5)] as number[] + + // in the ongoing year let's not include months that didn't happen yet + if (cur.year === lastYear) + return [...acc, ...cur[mode].slice(1, lastMonthIndex + 1)] as number[] + + return [...acc, ...cur[mode].slice(1)] as number[] + }, []) + + let nextMonth = 4 + let nextYear = 2018 + + return countsPutTogether.reduce( + (acc: { name: string; "Top 500 results": number }[], cur) => { + nextMonth++ + if (nextMonth === 13) { + nextMonth = 1 + nextYear++ + } + return [ + ...acc, + { name: `${months[nextMonth]} ${nextYear}`, "Top 500 results": cur }, + ] + }, + [] + ) + } + + const CustomTooltip: React.FC = ({ + active, + payload, + label, + }) => { + if (active) { + return ( + + + {payload[0].payload.name} + + + + {payload[0].payload["Top 500 results"]} + + + + ) + } + + return null + } + + return ( + + + } /> + + ) +} + +export default WeaponLineChart diff --git a/frontend-react/src/components/xtrends/XTrendsPage.tsx b/frontend-react/src/components/xtrends/XTrendsPage.tsx index 70865d726..b34fc98e5 100644 --- a/frontend-react/src/components/xtrends/XTrendsPage.tsx +++ b/frontend-react/src/components/xtrends/XTrendsPage.tsx @@ -14,14 +14,14 @@ import { PopoverTrigger, PopoverContent, PopoverArrow, - Badge, } from "@chakra-ui/core" import WeaponImage from "../common/WeaponImage" import MyThemeContext from "../../themeContext" import ModeButtons from "./ModeButtons" import Select from "../elements/Select" -import Button from "../elements/Button" -import { FaChartLine } from "react-icons/fa" +import WeaponLineChart from "./WeaponLineChart" +import { Helmet } from "react-helmet-async" +import Alert from "../elements/Alert" const tiers = [ { @@ -72,7 +72,7 @@ const tiers = [ ] as const const XTrendsPage: React.FC = () => { - const { grayWithShade, darkerBgColor, themeColor } = useContext( + const { grayWithShade, darkerBgColor, themeColorWithShade } = useContext( MyThemeContext ) const { data, error, loading } = useQuery(X_TRENDS) @@ -124,26 +124,33 @@ const XTrendsPage: React.FC = () => { return weapons.indexOf(a.name) - weapons.indexOf(b.name) }) + const xRankMonths = 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) + }) + return ( <> - + + + Top 500 Tier Lists | sendou.ink + + + + 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. + +