diff --git a/TODO.md b/TODO.md index 0c08dd79b..21456456a 100644 --- a/TODO.md +++ b/TODO.md @@ -2,9 +2,10 @@ - [x] Can log in - [x] Can update builds -- [ ] Build Analyzer +- [x] Build Analyzer - [x] Import /analyzer - - [ ] Builds link to /analyzer + - [x] Builds link to /analyzer +- [ ] Check that user page fallback / redirect works - [ ] Builds mode icons - [ ] Script to parse data from Top 500 .json to the DB - [ ] Top 500 Leaderboards diff --git a/components/builds/BuildCard.tsx b/components/builds/BuildCard.tsx index e823b5343..6784d67cf 100644 --- a/components/builds/BuildCard.tsx +++ b/components/builds/BuildCard.tsx @@ -41,7 +41,6 @@ const BuildCard: React.FC = ({ ...props }) => { const [apView, setApView] = useState(false); - const [showStats, setShowStats] = useState(false); const { themeColorShade, secondaryBgColor, gray } = useMyTheme(); @@ -49,9 +48,6 @@ const BuildCard: React.FC = ({ return ( <> - {/* {showStats && ( - setShowStats(false)} /> - )} */} = ({ icon={} mr="0.5em" /> - setShowStats(!showStats)} - aria-label="Show build stats view" - fontSize="20px" - icon={} - mr="0.5em" - /> + + + } + mr="0.5em" + /> + + {build.description && ( diff --git a/components/common/MyInfiniteScroller.tsx b/components/common/MyInfiniteScroller.tsx index 65187febe..4b20e0508 100644 --- a/components/common/MyInfiniteScroller.tsx +++ b/components/common/MyInfiniteScroller.tsx @@ -1,4 +1,5 @@ // TODO: this whole file should probably get replaced with something like react-window +// TODO: render less than 12? import { Flex } from "@chakra-ui/react"; import { ReactElement, ReactNodeArray, useState } from "react"; diff --git a/lib/lists/abilities.ts b/lib/lists/abilities.ts index bb37e7143..354d1ca90 100644 --- a/lib/lists/abilities.ts +++ b/lib/lists/abilities.ts @@ -61,3 +61,21 @@ export const isSlotOnlyAbility = ( (abilityObject) => abilityObject.code === ability && abilityObject.type === slot ); + +export function isAbilityArray( + arr: any[], + type: "HEAD" | "CLOTHING" | "SHOES" +) { + if (arr.length !== 4) return false; + + return arr.every((value, index) => { + const ability = abilities.find( + (abilityCodeInArray) => value === abilityCodeInArray.code + ); + if (!ability) return false; + + if (index === 0) return ["STACKABLE", type].includes(ability.type); + + return ability.type === "STACKABLE"; + }); +} diff --git a/lib/lists/weapons.ts b/lib/lists/weapons.ts index 8b6ecfff8..4e2380dbb 100644 --- a/lib/lists/weapons.ts +++ b/lib/lists/weapons.ts @@ -142,3 +142,5 @@ export const altWeaponToNormal = new Map([ ["Hero Brella Replica", "Splat Brella"], ["Octo Shot Replica", "Tentatek Splattershot"], ]); + +export const isWeapon = (value: any) => weapons.includes(value); diff --git a/lib/validators/build.ts b/lib/validators/build.ts index a8d592f6a..3c2d6b291 100644 --- a/lib/validators/build.ts +++ b/lib/validators/build.ts @@ -1,4 +1,4 @@ -import { abilities } from "lib/lists/abilities"; +import { isAbilityArray } from "lib/lists/abilities"; import { clothingGear, headGear, shoesGear } from "lib/lists/gear"; import { weaponsWithHero } from "lib/lists/weaponsWithHero"; import * as z from "zod"; @@ -75,18 +75,3 @@ export const buildSchema = z.object({ .nullable(), id: z.number().optional(), }); - -function isAbilityArray(arr: any[], type: "HEAD" | "CLOTHING" | "SHOES") { - if (arr.length !== 4) return false; - - return arr.every((value, index) => { - const ability = abilities.find( - (abilityCodeInArray) => value === abilityCodeInArray.code - ); - if (!ability) return false; - - if (index === 0) return ["STACKABLE", type].includes(ability.type); - - return ability.type === "STACKABLE"; - }); -} diff --git a/pages/analyzer.tsx b/pages/analyzer.tsx index b5ed4f501..a1120fc4e 100644 --- a/pages/analyzer.tsx +++ b/pages/analyzer.tsx @@ -13,10 +13,13 @@ import EditableBuilds from "components/analyzer/EditableBuilds"; import { ViewSlotsAbilities } from "components/builds/ViewSlots"; import Breadcrumbs from "components/common/Breadcrumbs"; import WeaponSelector from "components/common/WeaponSelector"; +import { isAbilityArray } from "lib/lists/abilities"; +import { isWeapon } from "lib/lists/weapons"; import { AbilityOrUnknown } from "lib/types"; import useAbilityEffects from "lib/useAbilityEffects"; import { useMyTheme } from "lib/useMyTheme"; -import { useState } from "react"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; import { FiSettings } from "react-icons/fi"; const CURRENT_PATCH = "5.3."; @@ -28,7 +31,8 @@ const defaultBuild: ViewSlotsAbilities = { }; const BuildAnalyzerPage = () => { - const { themeColorShade, gray } = useMyTheme(); + const router = useRouter(); + const { gray } = useMyTheme(); const [build, setBuild] = useState({ ...defaultBuild, }); @@ -65,36 +69,7 @@ const BuildAnalyzerPage = () => { lde: otherLde, }); - // function getBuildFromUrl() { - // const buildToReturn: AnalyzerBuild = { - // ...defaultBuild, - // weapon: "Splattershot Jr.", - // }; - // const decoded = new URLSearchParams(location.search); - - // for (const [key, value] of decoded) { - // switch (key) { - // case "weapon": - // buildToReturn.weapon = value; - // break; - // case "headgear": - // case "clothing": - // case "shoes": - // const abilityKey = key as "headgear" | "clothing" | "shoes"; - // buildToReturn[abilityKey] = value.split(",") as any; - // } - // } - - // return buildToReturn; - // } - - // useEffect(() => { - // const { weapon, ...buildFromUrl } = getBuildFromUrl(); - - // setWeapon(weapon); - // setBuild(buildFromUrl); - // // eslint-disable-next-line - // }, []); + useEffect(parseQuery, []); return ( <> @@ -200,6 +175,34 @@ const BuildAnalyzerPage = () => { ); + + function parseQuery() { + const query = router.query; + + const weapon = isWeapon(query.weapon) ? query.weapon : "Splattershot Jr."; + const headAbilities = + typeof query.head === "string" && + isAbilityArray(query.head.split(","), "HEAD") + ? query.head.split(",") + : ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"]; + const clothingAbilities = + typeof query.clothing === "string" && + isAbilityArray(query.clothing.split(","), "CLOTHING") + ? query.clothing.split(",") + : ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"]; + const shoesAbilities = + typeof query.shoes === "string" && + isAbilityArray(query.shoes.split(","), "SHOES") + ? query.shoes.split(",") + : ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"]; + + setWeapon(weapon as string); + setBuild({ + headAbilities: headAbilities as AbilityOrUnknown[], + clothingAbilities: clothingAbilities as AbilityOrUnknown[], + shoesAbilities: shoesAbilities as AbilityOrUnknown[], + }); + } }; export default BuildAnalyzerPage;