Get query states working on teams page

This changes the URL to show query params for our three filters, making it easy for people to link to very specific subsets of raids.
This commit is contained in:
Justin Edmund 2022-03-07 02:43:21 -08:00
parent 6d197e9f08
commit 1365e4c95c
5 changed files with 156 additions and 45 deletions

View file

@ -8,6 +8,7 @@ import RaidDropdown from '~components/RaidDropdown'
import { appState } from '~utils/appState'
import './index.scss'
import { raidGroups } from '~utils/raidGroups'
interface Props {
children: React.ReactNode
@ -15,7 +16,7 @@ interface Props {
element?: number
raidSlug?: string
recency?: number
onFilter: (element?: number, raid?: string, recency?: number) => void
onFilter: ({element, raid, recency} : { element?: number, raid?: Raid, recency?: number}) => void
}
const FilterBar = (props: Props) => {
@ -36,25 +37,26 @@ const FilterBar = (props: Props) => {
'shadow': props.scrolled
})
function selectChanged(event: React.ChangeEvent<HTMLSelectElement>) {
function elementSelectChanged() {
const elementValue = (elementSelect.current) ? parseInt(elementSelect.current.value) : -1
const recencyValue = (recencySelect.current) ? parseInt(recencySelect.current.value) : -1
let raidValue = ''
if (app.raids) {
const raid = app.raids.find((raid: Raid) => raid.slug === raidSelect.current?.value)
raidValue = (raid) ? raid.id : ''
props.onFilter({ element: elementValue })
}
props.onFilter(elementValue, raidValue, recencyValue)
function recencySelectChanged() {
const recencyValue = (recencySelect.current) ? parseInt(recencySelect.current.value) : -1
props.onFilter({ recency: recencyValue })
}
function raidSelectChanged(raid?: Raid) {
props.onFilter({ raid: raid })
}
return (
<div className={classes}>
{props.children}
<select onChange={selectChanged} ref={elementSelect} value={props.element}>
<select onChange={elementSelectChanged} ref={elementSelect} value={props.element}>
<option data-element="all" key={-1} value={-1}>{t('elements.full.all')}</option>
<option data-element="null" key={-0} value={0}>{t('elements.full.null')}</option>
<option data-element="null" key={0} value={0}>{t('elements.full.null')}</option>
<option data-element="wind" key={1} value={1}>{t('elements.full.wind')}</option>
<option data-element="fire" key={2} value={2}>{t('elements.full.fire')}</option>
<option data-element="water" key={3} value={3}>{t('elements.full.water')}</option>
@ -64,10 +66,10 @@ const FilterBar = (props: Props) => {
</select>
<RaidDropdown
showAllRaidsOption={true}
onChange={selectChanged}
onChange={raidSelectChanged}
ref={raidSelect}
/>
<select onChange={selectChanged} ref={recencySelect}>
<select onChange={recencySelectChanged} ref={recencySelect}>
<option key={-1} value={-1}>{t('recency.all_time')}</option>
<option key={86400} value={86400}>{t('recency.last_day')}</option>
<option key={604800} value={604800}>{t('recency.last_week')}</option>

View file

@ -11,7 +11,7 @@ import './index.scss'
interface Props {
showAllRaidsOption: boolean
currentRaid?: string
onChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void
onChange?: (raid?: Raid) => void
onBlur?: (event: React.ChangeEvent<HTMLSelectElement>) => void
}
@ -75,7 +75,7 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(function useFiel
function handleChange(event: React.ChangeEvent<HTMLSelectElement>) {
if (raids) {
const raid = raids.find(raid => raid.slug === event.target.value)
if (props.onChange) props.onChange(event)
if (props.onChange) props.onChange(raid)
setCurrentRaid(raid)
}
}

View file

@ -3,39 +3,77 @@ import Head from 'next/head'
import { useRouter } from 'next/router'
import { useCookies } from 'react-cookie'
import { queryTypes, useQueryState } from 'next-usequerystate'
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import clonedeep from 'lodash.clonedeep'
import api from '~utils/api'
import { elements, allElement } from '~utils/Element'
import GridRep from '~components/GridRep'
import GridRepCollection from '~components/GridRepCollection'
import FilterBar from '~components/FilterBar'
const TeamsRoute: React.FC = () => {
const router = useRouter()
const { t } = useTranslation('common')
// Cookies
// Set up cookies
const [cookies] = useCookies(['account'])
const headers = (cookies.account != null) ? {
'Authorization': `Bearer ${cookies.account.access_token}`
} : {}
// const { raids } = useSnapshot(appState)
// Get the information we need from the router
const router = useRouter()
// Import translations
const { t } = useTranslation('common')
// Set up app-specific states
const [loading, setLoading] = useState(true)
const [scrolled, setScrolled] = useState(false)
// Set up page-specific states
const [parties, setParties] = useState<Party[]>([])
const [raid, setRaid] = useState<Raid>()
// Filter states
const [element, setElement] = useState<number | null>(null)
const [raidId, setRaidId] = useState<string | null>(null)
const [recencyInSeconds, setRecencyInSeconds] = useState<number | null>(null)
// Set up filter-specific query states
// Recency is in seconds
const [element, setElement] = useQueryState("element", {
defaultValue: -1,
parse: (query: string) => parseElement(query),
serialize: value => serializeElement(value)
})
const [raidSlug, setRaidSlug] = useQueryState("raid", { defaultValue: "all" })
const [recency, setRecency] = useQueryState("recency", queryTypes.integer.withDefault(-1))
// Define transformers for element and raid
function parseElement(query: string) {
let element: TeamElement | undefined =
(query === 'all') ?
allElement : elements.find(element => element.name.en.toLowerCase() === query)
return (element) ? element.id : -1
}
function serializeElement(value: number | undefined) {
let name = ''
if (value != undefined) {
if (value == -1) {
name = allElement.name.en.toLowerCase()
} else {
console.log(value)
name = elements[value].name.en.toLowerCase()
}
}
return name
}
// Add scroll event listener for shadow on FilterBar
useEffect(() => {
window.addEventListener("scroll", handleScroll)
return () => window.removeEventListener("scroll", handleScroll);
@ -52,12 +90,14 @@ const TeamsRoute: React.FC = () => {
const fetchTeams = useCallback(() => {
const filters = {
params: {
element: element,
raid: raidId,
recency: recencyInSeconds
element: (element != -1) ? element : undefined,
raid: (raidSlug !== "all") ? raid?.id : undefined,
recency: (recency != -1) ? recency : undefined
}
}
console.log(filters)
const headers = (cookies.account) ? {
headers: {
'Authorization': `Bearer ${cookies.account.access_token}`
@ -77,27 +117,24 @@ const TeamsRoute: React.FC = () => {
setLoading(false)
})
.catch(error => handleError(error))
}, [element, raidId, recencyInSeconds, cookies.account, handleError])
}, [element, raid, recency, cookies.account, handleError])
useEffect(() => {
fetchTeams()
}, [fetchTeams])
function receiveFilters(element?: number, raid?: string, recency?: number) {
if (element != null && element >= 0)
function receiveFilters({ element, raid, recency }: {element?: number, raid?: Raid, recency?: number}) {
if (element == 0)
setElement(0)
else if (element)
setElement(element)
else
setElement(null)
if (raid && raid != '0')
setRaidId(raid)
else
setRaidId(null)
if (raid) {
setRaid(raid)
setRaidSlug(raid.slug)
}
if (recency && recency > 0)
setRecencyInSeconds(recency)
else
setRecencyInSeconds(null)
if (recency) setRecency(recency)
}
function toggleFavorite(teamId: string, favorited: boolean) {
@ -167,7 +204,13 @@ const TeamsRoute: React.FC = () => {
<meta name="twitter:title" content="Discover Teams" />
<meta name="twitter:description" content="Find different Granblue Fantasy teams by raid, element or recency" />
</Head>
<FilterBar onFilter={receiveFilters} scrolled={scrolled}>
<FilterBar
onFilter={receiveFilters}
scrolled={scrolled}
element={element}
raidSlug={raidSlug}
recency={recency}>
<h1>{t('teams.title')}</h1>
</FilterBar>

7
types/TeamElement.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
interface TeamElement {
id: number,
name: {
en: string,
ja: string
}
}

59
utils/Element.tsx Normal file
View file

@ -0,0 +1,59 @@
export const allElement: TeamElement = {
id: -1,
name: {
en: "All",
ja: "全s"
}
}
export const elements: TeamElement[] = [
{
id: 0,
name: {
en: "Null",
ja: "無"
}
},
{
id: 1,
name: {
en: "Wind",
ja: "風"
}
},
{
id: 2,
name: {
en: "Fire",
ja: "火"
}
},
{
id: 3,
name: {
en: "Water",
ja: "水"
}
},
{
id: 4,
name: {
en: "Earth",
ja: "土"
}
},
{
id: 5,
name: {
en: "Dark",
ja: "闇"
}
},
{
id: 6,
name: {
en: "Light",
ja: "光"
}
}
]