Fix page navigation when filtering collections

There was a bug that kept page navigation from working properly when filtering. Things would load multiple times, or load the wrong thing, or not navigate properly. That should all be fixed now.
This commit is contained in:
Justin Edmund 2023-07-04 00:28:05 -07:00
parent acbb10d458
commit a064cfa67c
8 changed files with 91 additions and 93 deletions

View file

@ -21,9 +21,10 @@ interface Props {
children: React.ReactNode children: React.ReactNode
scrolled: boolean scrolled: boolean
element?: number element?: number
raidSlug?: string raid?: string
recency?: number recency?: number
onFilter: (filters: FilterSet) => void onFilter: (filters: FilterSet) => void
onAdvancedFilter: (filters: FilterSet) => void
} }
const FilterBar = (props: Props) => { const FilterBar = (props: Props) => {
@ -55,10 +56,10 @@ const FilterBar = (props: Props) => {
const raid = appState.raidGroups const raid = appState.raidGroups
.filter((group) => group.section > 0) .filter((group) => group.section > 0)
.flatMap((group) => group.raids) .flatMap((group) => group.raids)
.find((raid) => raid.slug === props.raidSlug) .find((raid) => raid.id === props.raid)
setCurrentRaid(raid) setCurrentRaid(raid)
}, [props.raidSlug]) }, [props.raid])
useEffect(() => { useEffect(() => {
// Fetch user's advanced filters // Fetch user's advanced filters
@ -91,12 +92,12 @@ const FilterBar = (props: Props) => {
} }
function raidSelectChanged(raid?: Raid) { function raidSelectChanged(raid?: Raid) {
props.onFilter({ raidSlug: raid?.slug, ...advancedFilters }) props.onFilter({ raid: raid?.slug, ...advancedFilters })
} }
function handleAdvancedFiltersChanged(filters: FilterSet) { function handleAdvancedFiltersChanged(filters: FilterSet) {
setAdvancedFilters(filters) setAdvancedFilters(filters)
props.onFilter(filters) props.onAdvancedFilter(filters)
} }
function onSelectChange(name: 'element' | 'recency') { function onSelectChange(name: 'element' | 'recency') {

View file

@ -112,7 +112,7 @@ const RaidCombobox = (props: Props) => {
// Fetch all raids on mount // Fetch all raids on mount
useEffect(() => { useEffect(() => {
api.raidGroups().then((response) => sortGroups(response.data)) sortGroups(appState.raidGroups)
}, []) }, [])
// Set current raid and section when the component mounts // Set current raid and section when the component mounts
@ -136,6 +136,8 @@ const RaidCombobox = (props: Props) => {
if (appState.party.raid && appState.party.raid.group.section > 0) if (appState.party.raid && appState.party.raid.group.section > 0)
setCurrentSection(props.currentRaid.group.section) setCurrentSection(props.currentRaid.group.section)
else setCurrentSection(1) else setCurrentSection(1)
} else {
setCurrentRaid(undefined)
} }
}, [props.currentRaid]) }, [props.currentRaid])
@ -224,7 +226,9 @@ const RaidCombobox = (props: Props) => {
if (group.section > 0) sections[group.section - 1].push(group) if (group.section > 0) sections[group.section - 1].push(group)
}) })
if (groups[0]) {
setFarmingRaid(groups[0].raids[0]) setFarmingRaid(groups[0].raids[0])
}
setSections(sections) setSections(sections)
}, },

View file

@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component' import InfiniteScroll from 'react-infinite-scroll-component'
import { getCookie } from 'cookies-next'
import { queryTypes, useQueryState } from 'next-usequerystate' import { queryTypes, useQueryState } from 'next-usequerystate'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
@ -53,13 +52,12 @@ const ProfileRoute: React.FC<Props> = ({
const { t } = useTranslation('common') const { t } = useTranslation('common')
// Set up app-specific states // Set up app-specific states
const [raidsLoading, setRaidsLoading] = useState(true) const [mounted, setMounted] = useState(false)
const [scrolled, setScrolled] = useState(false) const [scrolled, setScrolled] = useState(false)
// Set up page-specific states // Set up page-specific states
const [parties, setParties] = useState<Party[]>([]) const [parties, setParties] = useState<Party[]>([])
const [raids, setRaids] = useState<Raid[]>() const [raids, setRaids] = useState<Raid[]>()
const [raid, setRaid] = useState<Raid>()
// Set up infinite scrolling-related states // Set up infinite scrolling-related states
const [recordCount, setRecordCount] = useState(0) const [recordCount, setRecordCount] = useState(0)
@ -74,9 +72,15 @@ const ProfileRoute: React.FC<Props> = ({
parse: (query: string) => parseElement(query), parse: (query: string) => parseElement(query),
serialize: (value) => serializeElement(value), serialize: (value) => serializeElement(value),
}) })
const [raidSlug, setRaidSlug] = useQueryState('raid', { const [raid, setRaid] = useQueryState('raid', {
defaultValue: 'all', defaultValue: 'all',
history: 'push', history: 'push',
parse: (query: string) => {
const raids = context?.raidGroups.flatMap((group) => group.raids)
const raid = raids?.find((r: Raid) => r.slug === query)
return raid ? raid.id : 'all'
},
serialize: (value) => value,
}) })
const [recency, setRecency] = useQueryState('recency', { const [recency, setRecency] = useQueryState('recency', {
defaultValue: -1, defaultValue: -1,
@ -113,6 +117,7 @@ const ProfileRoute: React.FC<Props> = ({
setTotalPages(context.pagination.totalPages) setTotalPages(context.pagination.totalPages)
setRecordCount(context.pagination.count) setRecordCount(context.pagination.count)
replaceResults(context.pagination.count, context.teams) replaceResults(context.pagination.count, context.teams)
appState.raidGroups = context.raidGroups
appState.version = version appState.version = version
} }
setCurrentPage(1) setCurrentPage(1)
@ -139,7 +144,7 @@ const ProfileRoute: React.FC<Props> = ({
const filters = { const filters = {
params: { params: {
element: element != -1 ? element : undefined, element: element != -1 ? element : undefined,
raid: raid ? raid.id : undefined, raid: raid === 'all' ? undefined : raid,
recency: recency != -1 ? recency : undefined, recency: recency != -1 ? recency : undefined,
page: currentPage, page: currentPage,
...advancedFilters, ...advancedFilters,
@ -183,14 +188,8 @@ const ProfileRoute: React.FC<Props> = ({
// Fetch all raids on mount, then find the raid in the URL if present // Fetch all raids on mount, then find the raid in the URL if present
useEffect(() => { useEffect(() => {
api.endpoints.raids.getAll().then((response) => { api.endpoints.raids.getAll().then((response) => {
setRaids(response.data) const raids = appState.raidGroups.flatMap((group) => group.raids)
setRaids(raids)
setRaidsLoading(false)
const raid = response.data.find((r: Raid) => r.slug === raidSlug)
setRaid(raid)
return raid
}) })
}, [setRaids]) }, [setRaids])
@ -198,7 +197,12 @@ const ProfileRoute: React.FC<Props> = ({
// fetch all teams again. // fetch all teams again.
useDidMountEffect(() => { useDidMountEffect(() => {
setCurrentPage(1) setCurrentPage(1)
if (mounted) {
fetchProfile({ replace: true }) fetchProfile({ replace: true })
}
setMounted(true)
}, [username, element, raid, recency, advancedFilters]) }, [username, element, raid, recency, advancedFilters])
// When the page changes, fetch all teams again. // When the page changes, fetch all teams again.
@ -206,25 +210,18 @@ const ProfileRoute: React.FC<Props> = ({
// Current page changed // Current page changed
if (currentPage > 1) fetchProfile({ replace: false }) if (currentPage > 1) fetchProfile({ replace: false })
else if (currentPage == 1) fetchProfile({ replace: true }) else if (currentPage == 1) fetchProfile({ replace: true })
setMounted(true)
}, [currentPage]) }, [currentPage])
// Receive filters from the filter bar // Receive filters from the filter bar
function receiveFilters(filters: FilterSet) { function receiveFilters(filters: FilterSet) {
if (filters.element == 0) setElement(0, { shallow: true }) if (filters.element == 0) setElement(0, { shallow: true })
else if (filters.element) setElement(filters.element, { shallow: true }) else if (filters.element) setElement(filters.element, { shallow: true })
if (filters.recency) setRecency(filters.recency, { shallow: true })
if (raids && filters.raidSlug) { if (filters.raid) setRaid(filters.raid, { shallow: true })
const raid = raids.find((raid) => raid.slug === filters.raidSlug)
setRaid(raid)
setRaidSlug(filters.raidSlug, { shallow: true })
} }
if (filters.recency) setRecency(filters.recency, { shallow: true }) function receiveAdvancedFilters(filters: FilterSet) {
delete filters.element
delete filters.raidSlug
delete filters.recency
setAdvancedFilters(filters) setAdvancedFilters(filters)
} }
@ -273,15 +270,16 @@ const ProfileRoute: React.FC<Props> = ({
if (context) { if (context) {
return ( return (
<div id="Profile"> <div className="profile">
{pageHead()} {pageHead()}
<FilterBar <FilterBar
defaultFilterset={permissiveFilterset} defaultFilterset={permissiveFilterset}
onAdvancedFilter={receiveAdvancedFilters}
onFilter={receiveFilters} onFilter={receiveFilters}
persistFilters={false} persistFilters={false}
scrolled={scrolled} scrolled={scrolled}
element={element} element={element}
raidSlug={raidSlug ? raidSlug : undefined} raid={raid}
recency={recency} recency={recency}
> >
<UserInfo user={context.user!} /> <UserInfo user={context.user!} />

View file

@ -25,6 +25,7 @@ import '../styles/globals.scss'
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
const { t } = useTranslation('common') const { t } = useTranslation('common')
const [mounted, setMounted] = useState(false)
const [refresh, setRefresh] = useState(false) const [refresh, setRefresh] = useState(false)
// Subscribe to app state to listen for account changes and // Subscribe to app state to listen for account changes and

View file

@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component' import InfiniteScroll from 'react-infinite-scroll-component'
import { getCookie } from 'cookies-next'
import { queryTypes, useQueryState } from 'next-usequerystate' import { queryTypes, useQueryState } from 'next-usequerystate'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
@ -52,13 +51,12 @@ const SavedRoute: React.FC<Props> = ({
const { t } = useTranslation('common') const { t } = useTranslation('common')
// Set up app-specific states // Set up app-specific states
const [raidsLoading, setRaidsLoading] = useState(true) const [mounted, setMounted] = useState(false)
const [scrolled, setScrolled] = useState(false) const [scrolled, setScrolled] = useState(false)
// Set up page-specific states // Set up page-specific states
const [parties, setParties] = useState<Party[]>([]) const [parties, setParties] = useState<Party[]>([])
const [raids, setRaids] = useState<Raid[]>() const [raids, setRaids] = useState<Raid[]>()
const [raid, setRaid] = useState<Raid>()
// Set up infinite scrolling-related states // Set up infinite scrolling-related states
const [recordCount, setRecordCount] = useState(0) const [recordCount, setRecordCount] = useState(0)
@ -73,9 +71,15 @@ const SavedRoute: React.FC<Props> = ({
parse: (query: string) => parseElement(query), parse: (query: string) => parseElement(query),
serialize: (value) => serializeElement(value), serialize: (value) => serializeElement(value),
}) })
const [raidSlug, setRaidSlug] = useQueryState('raid', { const [raid, setRaid] = useQueryState('raid', {
defaultValue: 'all', defaultValue: 'all',
history: 'push', history: 'push',
parse: (query: string) => {
const raids = context?.raidGroups.flatMap((group) => group.raids)
const raid = raids?.find((r: Raid) => r.slug === query)
return raid ? raid.id : 'all'
},
serialize: (value) => value,
}) })
const [recency, setRecency] = useQueryState('recency', { const [recency, setRecency] = useQueryState('recency', {
defaultValue: -1, defaultValue: -1,
@ -112,6 +116,7 @@ const SavedRoute: React.FC<Props> = ({
setTotalPages(context.pagination.totalPages) setTotalPages(context.pagination.totalPages)
setRecordCount(context.pagination.count) setRecordCount(context.pagination.count)
replaceResults(context.pagination.count, context.teams) replaceResults(context.pagination.count, context.teams)
appState.raidGroups = context.raidGroups
appState.version = version appState.version = version
} }
setCurrentPage(1) setCurrentPage(1)
@ -138,7 +143,7 @@ const SavedRoute: React.FC<Props> = ({
[key: string]: any [key: string]: any
} = { } = {
element: element !== -1 ? element : undefined, element: element !== -1 ? element : undefined,
raid: raid ? raid.id : undefined, raid: raid === 'all' ? undefined : raid,
recency: recency !== -1 ? recency : undefined, recency: recency !== -1 ? recency : undefined,
page: currentPage, page: currentPage,
...advancedFilters, ...advancedFilters,
@ -186,14 +191,8 @@ const SavedRoute: React.FC<Props> = ({
// Fetch all raids on mount, then find the raid in the URL if present // Fetch all raids on mount, then find the raid in the URL if present
useEffect(() => { useEffect(() => {
api.endpoints.raids.getAll().then((response) => { api.endpoints.raids.getAll().then((response) => {
setRaids(response.data) const raids = appState.raidGroups.flatMap((group) => group.raids)
setRaids(raids)
setRaidsLoading(false)
const raid = response.data.find((r: Raid) => r.slug === raidSlug)
setRaid(raid)
return raid
}) })
}, [setRaids]) }, [setRaids])
@ -201,7 +200,12 @@ const SavedRoute: React.FC<Props> = ({
// fetch all teams again. // fetch all teams again.
useDidMountEffect(() => { useDidMountEffect(() => {
setCurrentPage(1) setCurrentPage(1)
if (mounted) {
fetchTeams({ replace: true }) fetchTeams({ replace: true })
}
setMounted(true)
}, [element, raid, recency, advancedFilters]) }, [element, raid, recency, advancedFilters])
// When the page changes, fetch all teams again. // When the page changes, fetch all teams again.
@ -209,25 +213,18 @@ const SavedRoute: React.FC<Props> = ({
// Current page changed // Current page changed
if (currentPage > 1) fetchTeams({ replace: false }) if (currentPage > 1) fetchTeams({ replace: false })
else if (currentPage == 1) fetchTeams({ replace: true }) else if (currentPage == 1) fetchTeams({ replace: true })
setMounted(true)
}, [currentPage]) }, [currentPage])
// Receive filters from the filter bar // Receive filters from the filter bar
function receiveFilters(filters: FilterSet) { function receiveFilters(filters: FilterSet) {
if (filters.element == 0) setElement(0, { shallow: true }) if (filters.element == 0) setElement(0, { shallow: true })
else if (filters.element) setElement(filters.element, { shallow: true }) else if (filters.element) setElement(filters.element, { shallow: true })
if (filters.recency) setRecency(filters.recency, { shallow: true })
if (raids && filters.raidSlug) { if (filters.raid) setRaid(filters.raid, { shallow: true })
const raid = raids.find((raid) => raid.slug === filters.raidSlug)
setRaid(raid)
setRaidSlug(filters.raidSlug, { shallow: true })
} }
if (filters.recency) setRecency(filters.recency, { shallow: true }) function receiveAdvancedFilters(filters: FilterSet) {
delete filters.element
delete filters.raidSlug
delete filters.recency
setAdvancedFilters(filters) setAdvancedFilters(filters)
} }
@ -313,15 +310,16 @@ const SavedRoute: React.FC<Props> = ({
if (context) { if (context) {
return ( return (
<div id="Teams"> <div className="teams">
{pageHead()} {pageHead()}
<FilterBar <FilterBar
defaultFilterset={permissiveFilterset} defaultFilterset={permissiveFilterset}
onFilter={receiveFilters} onFilter={receiveFilters}
onAdvancedFilter={receiveAdvancedFilters}
persistFilters={false} persistFilters={false}
scrolled={scrolled} scrolled={scrolled}
element={element} element={element}
raidSlug={raidSlug ? raidSlug : undefined} raid={raid}
recency={recency} recency={recency}
> >
<h1>{t('saved.title')}</h1> <h1>{t('saved.title')}</h1>

View file

@ -52,13 +52,12 @@ const TeamsRoute: React.FC<Props> = ({
const { t } = useTranslation('common') const { t } = useTranslation('common')
// Set up app-specific states // Set up app-specific states
const [raidsLoading, setRaidsLoading] = useState(true) const [mounted, setMounted] = useState(false)
const [scrolled, setScrolled] = useState(false) const [scrolled, setScrolled] = useState(false)
// Set up page-specific states // Set up page-specific states
const [parties, setParties] = useState<Party[]>([]) const [parties, setParties] = useState<Party[]>([])
const [raids, setRaids] = useState<Raid[]>() const [raids, setRaids] = useState<Raid[]>()
const [raid, setRaid] = useState<Raid>()
// Set up infinite scrolling-related states // Set up infinite scrolling-related states
const [recordCount, setRecordCount] = useState(0) const [recordCount, setRecordCount] = useState(0)
@ -73,9 +72,15 @@ const TeamsRoute: React.FC<Props> = ({
parse: (query: string) => parseElement(query), parse: (query: string) => parseElement(query),
serialize: (value) => serializeElement(value), serialize: (value) => serializeElement(value),
}) })
const [raidSlug, setRaidSlug] = useQueryState('raid', { const [raid, setRaid] = useQueryState('raid', {
defaultValue: 'all', defaultValue: 'all',
history: 'push', history: 'push',
parse: (query: string) => {
const raids = context?.raidGroups.flatMap((group) => group.raids)
const raid = raids?.find((r: Raid) => r.slug === query)
return raid ? raid.id : 'all'
},
serialize: (value) => value,
}) })
const [recency, setRecency] = useQueryState('recency', { const [recency, setRecency] = useQueryState('recency', {
defaultValue: -1, defaultValue: -1,
@ -149,7 +154,7 @@ const TeamsRoute: React.FC<Props> = ({
[key: string]: any [key: string]: any
} = { } = {
element: element !== -1 ? element : undefined, element: element !== -1 ? element : undefined,
raid: raid ? raid.id : undefined, raid: raid === 'all' ? undefined : raid,
recency: recency !== -1 ? recency : undefined, recency: recency !== -1 ? recency : undefined,
page: currentPage, page: currentPage,
...advancedFilters, ...advancedFilters,
@ -196,49 +201,39 @@ const TeamsRoute: React.FC<Props> = ({
// Fetch all raids on mount, then find the raid in the URL if present // Fetch all raids on mount, then find the raid in the URL if present
useEffect(() => { useEffect(() => {
api.endpoints.raids.getAll().then((response) => { const raids = appState.raidGroups.flatMap((group) => group.raids)
setRaids(response.data) setRaids(raids)
setRaidsLoading(false)
const raid = response.data.find((r: Raid) => r.slug === raidSlug)
setRaid(raid)
return raid
})
}, [setRaids]) }, [setRaids])
// When the element, raid or recency filter changes, // When the element, raid or recency filter changes,
// fetch all teams again. // fetch all teams again.
useDidMountEffect(() => { useDidMountEffect(() => {
setCurrentPage(1) setCurrentPage(1)
if (mounted) {
fetchTeams({ replace: true }) fetchTeams({ replace: true })
}
setMounted(true)
}, [element, raid, recency, advancedFilters]) }, [element, raid, recency, advancedFilters])
// When the page changes, fetch all teams again. // When the page changes, fetch all teams again.
useDidMountEffect(() => { useDidMountEffect(() => {
// Current page changed // Current page changed
if (currentPage > 1) fetchTeams({ replace: false }) if (currentPage > 1) fetchTeams({ replace: false })
else if (currentPage == 1) fetchTeams({ replace: true }) else if (currentPage == 1 && mounted) fetchTeams({ replace: true })
setMounted(true)
}, [currentPage]) }, [currentPage])
// Receive filters from the filter bar // Receive filters from the filter bar
function receiveFilters(filters: FilterSet) { function receiveFilters(filters: FilterSet) {
if (filters.element == 0) setElement(0, { shallow: true }) if (filters.element == 0) setElement(0, { shallow: true })
else if (filters.element) setElement(filters.element, { shallow: true }) else if (filters.element) setElement(filters.element, { shallow: true })
if (filters.recency) setRecency(filters.recency, { shallow: true })
if (raids && filters.raidSlug) { if (filters.raid) setRaid(filters.raid, { shallow: true })
const raid = raids.find((raid) => raid.slug === filters.raidSlug)
setRaid(raid)
setRaidSlug(filters.raidSlug, { shallow: true })
} }
if (filters.recency) setRecency(filters.recency, { shallow: true }) function receiveAdvancedFilters(filters: FilterSet) {
delete filters.element
delete filters.raidSlug
delete filters.recency
setAdvancedFilters(filters) setAdvancedFilters(filters)
} }
@ -324,15 +319,16 @@ const TeamsRoute: React.FC<Props> = ({
if (context) { if (context) {
return ( return (
<div id="Teams"> <div className="teams">
{pageHead()} {pageHead()}
<FilterBar <FilterBar
defaultFilterset={defaultFilterset} defaultFilterset={defaultFilterset}
onFilter={receiveFilters} onFilter={receiveFilters}
onAdvancedFilter={receiveAdvancedFilters}
persistFilters={true} persistFilters={true}
scrolled={scrolled} scrolled={scrolled}
element={element} element={element}
raidSlug={raidSlug ? raidSlug : undefined} raid={raid}
recency={recency} recency={recency}
> >
<h1>{t('teams.title')}</h1> <h1>{t('teams.title')}</h1>

View file

@ -121,8 +121,8 @@ h5 {
} }
// Used for collection pages // Used for collection pages
#Teams, .teams,
#Profile { .profile {
display: flex; display: flex;
height: 100%; height: 100%;
flex-direction: column; flex-direction: column;

View file

@ -1,6 +1,6 @@
interface FilterSet { interface FilterSet {
element?: number element?: number
raidSlug?: string raid?: string
recency?: number recency?: number
full_auto?: number full_auto?: number
auto_guard?: number auto_guard?: number