mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 23:19:39 -05:00
builds stuff
This commit is contained in:
parent
9d422a0858
commit
2071a3beeb
16
frontend-react/package-lock.json
generated
16
frontend-react/package-lock.json
generated
|
|
@ -1927,6 +1927,14 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-infinite-scroller": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-infinite-scroller/-/react-infinite-scroller-1.2.1.tgz",
|
||||
"integrity": "sha512-64bpbqdSgtmy1zSZ2AQoFzguwZO7TyKjqJRTEnfNMCAQbnrX90kz+rYufZyY9CmzhwpXMwRO8xR9fMQnbYUkgQ==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
||||
|
|
@ -11846,6 +11854,14 @@
|
|||
"camelcase": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"react-infinite-scroller": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.4.tgz",
|
||||
"integrity": "sha512-/oOa0QhZjXPqaD6sictN2edFMsd3kkMiE19Vcz5JDgHpzEJVqYcmq+V3mkwO88087kvKGe1URNksHEOt839Ubw==",
|
||||
"requires": {
|
||||
"prop-types": "^15.5.8"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.12.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"@types/reach__router": "^1.2.6",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@types/react-infinite-scroller": "^1.2.1",
|
||||
"apollo-boost": "^0.4.7",
|
||||
"emotion-theming": "^10.0.27",
|
||||
"graphql": "^14.5.8",
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
"react-helmet-async": "^1.0.4",
|
||||
"react-hotkeys-hook": "^1.5.4",
|
||||
"react-icons": "^3.8.0",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-scripts": "3.3.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useContext } from "react"
|
||||
import React, { useState, useContext, useEffect } from "react"
|
||||
import { Build } from "../../types"
|
||||
import {
|
||||
Box,
|
||||
|
|
@ -16,20 +16,31 @@ import { FaInfo, FaPlus, FaMinus } from "react-icons/fa"
|
|||
import ViewGear from "./ViewGear"
|
||||
import ViewAP from "./ViewAP"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import { Link } from "@reach/router"
|
||||
|
||||
interface BuildCardProps {
|
||||
build: Build
|
||||
defaultToAPView: Boolean
|
||||
defaultToAPView: boolean
|
||||
showUser?: boolean
|
||||
}
|
||||
|
||||
const BuildCard: React.FC<BuildCardProps> = ({ build, defaultToAPView }) => {
|
||||
const BuildCard: React.FC<BuildCardProps> = ({
|
||||
build,
|
||||
defaultToAPView,
|
||||
showUser = false,
|
||||
}) => {
|
||||
const [apView, setApView] = useState(defaultToAPView)
|
||||
const { borderStyle, themeColor, darkerBgColor, grayWithShade } = useContext(
|
||||
MyThemeContext
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setApView(defaultToAPView)
|
||||
}, [defaultToAPView])
|
||||
|
||||
return (
|
||||
<Box
|
||||
as="fieldset"
|
||||
width="325px"
|
||||
borderWidth="1px"
|
||||
border={borderStyle}
|
||||
|
|
@ -39,6 +50,19 @@ const BuildCard: React.FC<BuildCardProps> = ({ build, defaultToAPView }) => {
|
|||
pb="6"
|
||||
px="6"
|
||||
>
|
||||
{showUser && build.discord_user && (
|
||||
<Box
|
||||
as="legend"
|
||||
color={grayWithShade}
|
||||
fontWeight="semibold"
|
||||
letterSpacing="wide"
|
||||
fontSize="s"
|
||||
>
|
||||
<Link to={`/u/${build.discord_user.discord_id}`}>
|
||||
{build.discord_user.username}#{build.discord_user.discriminator}
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
<Flex justifyContent="space-between">
|
||||
<Box width="24" height="auto">
|
||||
<WeaponImage englishName={build.weapon} size="MEDIUM" />
|
||||
|
|
@ -58,11 +82,12 @@ const BuildCard: React.FC<BuildCardProps> = ({ build, defaultToAPView }) => {
|
|||
fontWeight="semibold"
|
||||
letterSpacing="wide"
|
||||
fontSize="xs"
|
||||
ml="8px"
|
||||
>
|
||||
{new Date(parseInt(build.updatedAt)).toLocaleString()}
|
||||
</Box>
|
||||
{build.title && (
|
||||
<Box mt="1" fontWeight="semibold" as="h4" lineHeight="tight">
|
||||
<Box ml="8px" fontWeight="semibold" as="h4" lineHeight="tight">
|
||||
{build.title}
|
||||
</Box>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -3,50 +3,88 @@ import { Helmet } from "react-helmet-async"
|
|||
import { RouteComponentProps } from "@reach/router"
|
||||
import WeaponSelector from "../common/WeaponSelector"
|
||||
import { useState } from "react"
|
||||
import { Weapon, Ability } from "../../types"
|
||||
import {
|
||||
Weapon,
|
||||
Ability,
|
||||
SearchForBuildsData,
|
||||
SearchForBuildsVars,
|
||||
Build,
|
||||
} from "../../types"
|
||||
import useBreakPoints from "../../hooks/useBreakPoints"
|
||||
import { abilitiesGameOrder } from "../../utils/lists"
|
||||
import { Box, Flex, Heading, FormLabel, Switch } from "@chakra-ui/core"
|
||||
import { Box, Flex, Heading, FormLabel, Switch, Button } from "@chakra-ui/core"
|
||||
import AbilityIcon from "./AbilityIcon"
|
||||
import { useContext } from "react"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import useLocalStorage from "@rehooks/local-storage"
|
||||
import { useQuery } from "@apollo/react-hooks"
|
||||
import { SEARCH_FOR_BUILDS } from "../../graphql/queries/searchForBuilds"
|
||||
import Loading from "../common/Loading"
|
||||
import Error from "../common/Error"
|
||||
import BuildCard from "./BuildCard"
|
||||
import InfiniteScroll from "react-infinite-scroller"
|
||||
|
||||
const BuildsPage: React.FC<RouteComponentProps> = () => {
|
||||
const { themeColor } = useContext(MyThemeContext)
|
||||
const [weapon, setWeapon] = useState<Weapon | null>(null)
|
||||
const [abilities, setAbilities] = useState<Ability[]>(["T"])
|
||||
const [buildsToShow, setBuildsToShow] = useState(4)
|
||||
const [abilities, setAbilities] = useState<Ability[]>([])
|
||||
const [prefersAPView, setAPPreference] = useLocalStorage<boolean>(
|
||||
"prefersAPView"
|
||||
)
|
||||
console.log("prefersAPView", prefersAPView)
|
||||
const isSmall = useBreakPoints(870)
|
||||
|
||||
const { data, error, loading } = useQuery<
|
||||
SearchForBuildsData,
|
||||
SearchForBuildsVars
|
||||
>(SEARCH_FOR_BUILDS, {
|
||||
variables: { weapon: weapon as any },
|
||||
skip: !weapon,
|
||||
})
|
||||
if (error) return <Error errorMessage={error.message} />
|
||||
|
||||
const buildsFilterByAbilities: Build[] = !data
|
||||
? []
|
||||
: data.searchForBuilds.filter(build => {
|
||||
if (abilities.length === 0) return true
|
||||
const abilitiesInBuild = new Set([
|
||||
...build.headgear,
|
||||
...build.clothing,
|
||||
...build.shoes,
|
||||
])
|
||||
return abilities.every(ability => abilitiesInBuild.has(ability))
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Builds | sendou.ink</title>
|
||||
<title>{weapon ? `${weapon} ` : ""}Builds | sendou.ink</title>
|
||||
</Helmet>
|
||||
<FormLabel htmlFor="apview">Default to Ability Point view</FormLabel>
|
||||
<Switch
|
||||
id="apview"
|
||||
color={themeColor}
|
||||
isChecked={prefersAPView === null ? false : prefersAPView}
|
||||
onChange={() => setAPPreference(!prefersAPView)}
|
||||
/>
|
||||
<Box mb="1em">
|
||||
<FormLabel htmlFor="apview">Default to Ability Point view</FormLabel>
|
||||
<Switch
|
||||
id="apview"
|
||||
color={themeColor}
|
||||
isChecked={prefersAPView === null ? false : prefersAPView}
|
||||
onChange={() => setAPPreference(!prefersAPView)}
|
||||
/>
|
||||
</Box>
|
||||
<WeaponSelector
|
||||
weapon={weapon}
|
||||
setWeapon={(weapon: Weapon | null) => setWeapon(weapon)}
|
||||
dropdownMode={isSmall}
|
||||
/>
|
||||
{!weapon && (
|
||||
<Flex flexWrap="wrap" justifyContent="center" pt="1em">
|
||||
{weapon && (
|
||||
<Flex flexWrap="wrap" justifyContent="center" mt="1em">
|
||||
{abilitiesGameOrder.map(ability => (
|
||||
<Box
|
||||
key={ability}
|
||||
p="5px"
|
||||
cursor="pointer"
|
||||
onClick={() => setAbilities(abilities.concat(ability))}
|
||||
cursor={abilities.indexOf(ability) === -1 ? "pointer" : undefined}
|
||||
onClick={() => {
|
||||
if (abilities.indexOf(ability) !== -1) return
|
||||
setAbilities(abilities.concat(ability))
|
||||
}}
|
||||
>
|
||||
<AbilityIcon
|
||||
ability={abilities.indexOf(ability) === -1 ? ability : "EMPTY"}
|
||||
|
|
@ -81,6 +119,43 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
|
|||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{loading && <Loading />}
|
||||
{buildsFilterByAbilities.length > 0 && data && (
|
||||
<>
|
||||
<InfiniteScroll
|
||||
pageStart={1}
|
||||
loadMore={page => setBuildsToShow(page * 4)}
|
||||
hasMore={buildsToShow < data.searchForBuilds.length}
|
||||
>
|
||||
<Flex flexWrap="wrap" pt="2em">
|
||||
{buildsFilterByAbilities
|
||||
.filter((build, index) => index < buildsToShow)
|
||||
.map(build => (
|
||||
<Box key={build.id} p="0.2em">
|
||||
<BuildCard
|
||||
build={build}
|
||||
defaultToAPView={
|
||||
prefersAPView === null ? false : prefersAPView
|
||||
}
|
||||
showUser
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
</InfiniteScroll>
|
||||
<Box w="50%" textAlign="center" mx="auto">
|
||||
<Heading size="sm">No more builds to show</Heading>
|
||||
<Button
|
||||
variantColor={themeColor}
|
||||
variant="outline"
|
||||
mt="1em"
|
||||
onClick={() => window.scrollTo(0, 0)}
|
||||
>
|
||||
Return to the top
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const ViewGear: React.FC<ViewGearProps> = ({ build }) => {
|
|||
gridRowGap="10px"
|
||||
justifyItems="center"
|
||||
alignItems="center"
|
||||
mt={noItems ? "2" : "0"}
|
||||
mt="1em"
|
||||
>
|
||||
<GearImage
|
||||
englishName={build.headgearItem}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Box } from "@chakra-ui/core"
|
|||
import MyThemeContext from "../../themeContext"
|
||||
|
||||
interface DividingBoxProps {
|
||||
children: JSX.Element | JSX.Element[] | string
|
||||
children: React.ReactNode
|
||||
location: "top" | "left" | "bottom" | "right"
|
||||
margin?: string
|
||||
width?: string
|
||||
|
|
|
|||
41
frontend-react/src/components/common/FieldsetWithLegend.tsx
Normal file
41
frontend-react/src/components/common/FieldsetWithLegend.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react"
|
||||
import { Box, BoxProps } from "@chakra-ui/core"
|
||||
import { useContext } from "react"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
|
||||
interface FieldsetWithLegendProps {
|
||||
children: React.ReactNode
|
||||
title: string
|
||||
titleFontSize: string
|
||||
}
|
||||
|
||||
const FieldsetWithLegend: React.FC<FieldsetWithLegendProps> = ({
|
||||
children,
|
||||
title,
|
||||
titleFontSize,
|
||||
}) => {
|
||||
const { borderStyle, grayWithShade } = useContext(MyThemeContext)
|
||||
return (
|
||||
<Box
|
||||
as="fieldset"
|
||||
maxW="sm"
|
||||
border={borderStyle}
|
||||
rounded="lg"
|
||||
display="inline-block"
|
||||
p="1em"
|
||||
>
|
||||
<Box
|
||||
as="legend"
|
||||
color={grayWithShade}
|
||||
fontWeight="semibold"
|
||||
letterSpacing="wide"
|
||||
fontSize={titleFontSize}
|
||||
>
|
||||
{title}
|
||||
</Box>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default FieldsetWithLegend
|
||||
|
|
@ -7,7 +7,7 @@ interface LoadingProps {}
|
|||
const Loading: React.FC<LoadingProps> = () => {
|
||||
const { themeColorWithShade } = useContext(MyThemeContext)
|
||||
return (
|
||||
<Box textAlign="center">
|
||||
<Box textAlign="center" pt="2em">
|
||||
<Spinner
|
||||
color={themeColorWithShade}
|
||||
size="xl"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react"
|
||||
import { Weapon } from "../../types"
|
||||
import { Input, Flex, PseudoBox, Select } from "@chakra-ui/core"
|
||||
import { Input, Flex, PseudoBox, Select, Box } from "@chakra-ui/core"
|
||||
import { useState } from "react"
|
||||
import { weapons } from "../../utils/lists"
|
||||
import WeaponImage from "./WeaponImage"
|
||||
|
|
@ -18,7 +18,9 @@ const WeaponSelector: React.FC<WeaponSelectorProps> = ({
|
|||
setWeapon,
|
||||
dropdownMode = false,
|
||||
}) => {
|
||||
const { darkerBgColor } = useContext(MyThemeContext)
|
||||
const { darkerBgColor, borderStyle, grayWithShade } = useContext(
|
||||
MyThemeContext
|
||||
)
|
||||
const [input, setInput] = useState("")
|
||||
|
||||
const filterWeaponArray = (weapon: Weapon) =>
|
||||
|
|
@ -30,7 +32,7 @@ const WeaponSelector: React.FC<WeaponSelectorProps> = ({
|
|||
if (dropdownMode)
|
||||
return (
|
||||
<Select
|
||||
placeholder="Select a weapon"
|
||||
placeholder="Filter weapons"
|
||||
value={weapon ?? ""}
|
||||
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
||||
setWeapon(event.target.value as Weapon)
|
||||
|
|
@ -50,40 +52,62 @@ const WeaponSelector: React.FC<WeaponSelectorProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
placeholder="Click below or search for a weapon"
|
||||
value={input}
|
||||
onChange={handleInputChange}
|
||||
w="50%"
|
||||
ml="auto"
|
||||
mr="auto"
|
||||
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key !== "Enter") return
|
||||
const oneWeaponArray = weapons.filter(filterWeaponArray)
|
||||
if (oneWeaponArray.length !== 1) return
|
||||
setWeapon(oneWeaponArray[0])
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
<Flex flexWrap="wrap" justifyContent="center" pt="1em">
|
||||
{weapons.filter(filterWeaponArray).map(weapon => (
|
||||
<PseudoBox
|
||||
key={weapon}
|
||||
px="3px"
|
||||
py="2px"
|
||||
cursor="pointer"
|
||||
onClick={() => setWeapon(weapon)}
|
||||
userSelect="none"
|
||||
_hover={{
|
||||
bg: "rgba(128, 128, 128, 0.3)",
|
||||
borderRadius: "50%",
|
||||
transition: "background-color .5s",
|
||||
}}
|
||||
>
|
||||
<WeaponImage englishName={weapon} size="SMALL" />
|
||||
</PseudoBox>
|
||||
))}
|
||||
</Flex>
|
||||
<Box pb="1em">
|
||||
<Input
|
||||
placeholder="Filter weapons"
|
||||
value={input}
|
||||
onChange={handleInputChange}
|
||||
w="50%"
|
||||
ml="auto"
|
||||
mr="auto"
|
||||
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key !== "Enter") return
|
||||
const oneWeaponArray = weapons.filter(filterWeaponArray)
|
||||
if (oneWeaponArray.length !== 1) return
|
||||
setWeapon(oneWeaponArray[0])
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
as="fieldset"
|
||||
border={borderStyle}
|
||||
borderWidth="1px"
|
||||
overflow="hidden"
|
||||
borderX="none"
|
||||
borderBottom="none"
|
||||
>
|
||||
<Box
|
||||
as="legend"
|
||||
color={grayWithShade}
|
||||
fontWeight="semibold"
|
||||
letterSpacing="wide"
|
||||
fontSize="md"
|
||||
textAlign="center"
|
||||
px="5px"
|
||||
>
|
||||
Click a weapon to select it
|
||||
</Box>
|
||||
<Flex flexWrap="wrap" justifyContent="center" p="0.5em">
|
||||
{weapons.filter(filterWeaponArray).map(weapon => (
|
||||
<PseudoBox
|
||||
key={weapon}
|
||||
px="3px"
|
||||
py="2px"
|
||||
cursor="pointer"
|
||||
onClick={() => setWeapon(weapon)}
|
||||
userSelect="none"
|
||||
_hover={{
|
||||
bg: "rgba(128, 128, 128, 0.3)",
|
||||
borderRadius: "50%",
|
||||
transition: "background-color .5s",
|
||||
}}
|
||||
>
|
||||
<WeaponImage englishName={weapon} size="SMALL" />
|
||||
</PseudoBox>
|
||||
))}
|
||||
</Flex>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,9 @@ const UserPage: React.FC<RouteComponentProps & UserPageProps> = ({ id }) => {
|
|||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{user.username} | sendou.ink</title>
|
||||
<title>
|
||||
{user.username}#{user.discriminator} | sendou.ink
|
||||
</title>
|
||||
</Helmet>
|
||||
<AvatarWithInfo user={user} />
|
||||
{user.weapons && user.weapons.length > 0 && (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useContext } from "react"
|
||||
import React from "react"
|
||||
import { Weapon } from "../../types"
|
||||
import { Box, Flex } from "@chakra-ui/core"
|
||||
import WeaponImage from "../common/WeaponImage"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import FieldsetWithLegend from "../common/FieldsetWithLegend"
|
||||
|
||||
interface WeaponPoolProps {
|
||||
weapons: Weapon[]
|
||||
|
|
@ -14,29 +14,8 @@ const styles = {
|
|||
} as const
|
||||
|
||||
const WeaponPool: React.FC<WeaponPoolProps> = ({ weapons }) => {
|
||||
const { colorMode, grayWithShade } = useContext(MyThemeContext)
|
||||
const borderStyle: string = styles[colorMode]
|
||||
|
||||
return (
|
||||
<Box
|
||||
as="fieldset"
|
||||
maxW="sm"
|
||||
border={borderStyle}
|
||||
rounded="lg"
|
||||
overflow="hidden"
|
||||
display="inline-block"
|
||||
p="1em"
|
||||
>
|
||||
<Box
|
||||
as="legend"
|
||||
color={grayWithShade}
|
||||
fontWeight="semibold"
|
||||
letterSpacing="wide"
|
||||
fontSize="xs"
|
||||
textTransform="uppercase"
|
||||
>
|
||||
Weapon pool
|
||||
</Box>
|
||||
<FieldsetWithLegend title="WEAPON POOL" titleFontSize="xs">
|
||||
<Flex>
|
||||
{weapons.map(wpn => (
|
||||
<Box mx="0.3em" key={wpn}>
|
||||
|
|
@ -44,7 +23,7 @@ const WeaponPool: React.FC<WeaponPoolProps> = ({ weapons }) => {
|
|||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
</Box>
|
||||
</FieldsetWithLegend>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { gql, DocumentNode } from "apollo-boost"
|
||||
|
||||
export const SEARCH_FOR_BUILDS: DocumentNode = gql`
|
||||
query searchForBuilds($discord_id: String!) {
|
||||
searchForBuilds(discord_id: $discord_id) {
|
||||
query searchForBuilds($discord_id: String, $weapon: String) {
|
||||
searchForBuilds(discord_id: $discord_id, weapon: $weapon) {
|
||||
id
|
||||
weapon
|
||||
title
|
||||
|
|
@ -15,6 +15,11 @@ export const SEARCH_FOR_BUILDS: DocumentNode = gql`
|
|||
shoesItem
|
||||
updatedAt
|
||||
top
|
||||
discord_user {
|
||||
username
|
||||
discriminator
|
||||
discord_id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
|||
|
|
@ -110,6 +110,11 @@ export interface Build {
|
|||
shoesItem?: ShoesGear
|
||||
updatedAt: string
|
||||
top: boolean
|
||||
discord_user?: {
|
||||
username: string
|
||||
discriminator: string
|
||||
discord_id: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface Placement {
|
||||
|
|
@ -145,7 +150,8 @@ export interface SearchForBuildsData {
|
|||
}
|
||||
|
||||
export interface SearchForBuildsVars {
|
||||
discord_id: string
|
||||
discord_id?: string
|
||||
weapon?: Weapon
|
||||
}
|
||||
|
||||
export interface PlayerInfoData {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const gear = require("../utils/gear")
|
|||
|
||||
const typeDef = gql`
|
||||
extend type Query {
|
||||
searchForBuilds(discord_id: String!): [Build!]!
|
||||
searchForBuilds(discord_id: String, weapon: String): [Build!]!
|
||||
"Returns builds by given weapon. If weapon is omitted returns latest builds instead."
|
||||
searchForBuildsByWeapon(weapon: String, page: Int): BuildCollection!
|
||||
}
|
||||
|
|
@ -93,8 +93,13 @@ const typeDef = gql`
|
|||
const resolvers = {
|
||||
Query: {
|
||||
searchForBuilds: (root, args) => {
|
||||
return Build.find({ discord_id: args.discord_id })
|
||||
.sort({ weapon: "asc" })
|
||||
if (!args.discord_id && !args.weapon)
|
||||
throw new UserInputError(
|
||||
"Discord ID or weapon has to be in the arguments"
|
||||
)
|
||||
return Build.find({ ...args })
|
||||
.sort({ top: "desc", updatedAt: "desc" })
|
||||
.populate("discord_user")
|
||||
.catch(e => {
|
||||
throw new UserInputError(e.message, {
|
||||
invalidArgs: args,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ const typeDef = gql`
|
|||
username: String!
|
||||
"Discord discriminator. For example with Sendou#0043 0043 is the discriminator."
|
||||
discriminator: String!
|
||||
"String that allows finding users avatar on Discord."
|
||||
discord_id: String!
|
||||
twitch_name: String
|
||||
twitter_name: String
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user