diff --git a/site/src/pages/Bounties/index.jsx b/site/src/pages/Bounties/index.jsx index e036109fa..40855d56e 100644 --- a/site/src/pages/Bounties/index.jsx +++ b/site/src/pages/Bounties/index.jsx @@ -1,4 +1,3 @@ -// @ts-check import React, { useCallback, useEffect, useMemo, useState } from "react"; import Nav from "./Nav"; @@ -26,6 +25,7 @@ import Divider from "../../components/Divider"; import Filter from "../../components/Filter"; import useListFilter from "../../components/Filter/useListFilter"; import styled from "styled-components"; +import { useHistory } from "react-router"; const QUERY_PAGE_KEY = "page"; @@ -59,6 +59,7 @@ const Bounties = () => { DEFAULT_PAGE_SIZE, ); const sort = query.get("sort"); + const history = useHistory(); const { filterStatus, @@ -79,12 +80,33 @@ const Bounties = () => { const chain = useSelector(chainSelector); useEffect(() => { + const searchParams = new URLSearchParams(history.location.search); + searchParams.delete("page"); + history.push({ search: searchParams.toString() }); + + setTablePage(DEFAULT_QUERY_PAGE); + }, [getFilterData]); + + useEffect(() => { + const controller = new AbortController(); + const filterData = getFilterData(); + + const params = { + ...filterData, + }; + if (sort) { + params.sort = sort; + } + dispatch( - fetchBounties(tablePage - 1, pageSize, filterData, sort && { sort }), + fetchBounties(tablePage - 1, pageSize, params, { + signal: controller.signal, + }), ); return () => { + controller.abort(); dispatch(resetBounties()); }; }, [dispatch, tablePage, pageSize, getFilterData, sort]); diff --git a/site/src/pages/ChildBounties/index.jsx b/site/src/pages/ChildBounties/index.jsx index ee4ca7358..5ff285097 100644 --- a/site/src/pages/ChildBounties/index.jsx +++ b/site/src/pages/ChildBounties/index.jsx @@ -1,4 +1,3 @@ -// @ts-check import React, { useEffect, useMemo, useState } from "react"; import Pagination from "../Bounties/Pagination"; @@ -24,6 +23,7 @@ import styled from "styled-components"; import Divider from "../../components/Divider"; import Filter from "../../components/Filter"; import useListFilter from "../../components/Filter/useListFilter"; +import { useHistory } from "react-router"; const QUERY_PAGE_KEY = "page"; @@ -43,9 +43,11 @@ const FilterWrapper = styled.div` padding: 24px; `; -const ChildBounties = () => { +function ChildBounties() { const query = useQuery(); + const history = useHistory(); + const searchPage = parseInt(query.get(QUERY_PAGE_KEY) || "1"); const queryPage = searchPage && !isNaN(searchPage) && searchPage > 0 @@ -76,12 +78,33 @@ const ChildBounties = () => { } = useListFilter(); useEffect(() => { + const searchParams = new URLSearchParams(history.location.search); + searchParams.delete("page"); + history.push({ search: searchParams.toString() }); + + setTablePage(DEFAULT_QUERY_PAGE); + }, [getFilterData]); + + useEffect(() => { + const controller = new AbortController(); + const filterData = getFilterData(); + + const params = { + ...filterData, + }; + if (sort) { + params.sort = sort; + } + dispatch( - fetchChildBounties(tablePage - 1, pageSize, filterData, sort && { sort }), + fetchChildBounties(tablePage - 1, pageSize, params, { + signal: controller.signal, + }), ); return () => { + controller.abort(); dispatch(resetChildBounties()); }; }, [dispatch, tablePage, pageSize, getFilterData, sort]); @@ -92,7 +115,7 @@ const ChildBounties = () => { ); const tableData = useMemo( - () => childBounties.map(compatChildBountyData), + () => childBounties?.map(compatChildBountyData), [childBounties], ); @@ -139,6 +162,6 @@ const ChildBounties = () => { footer={footer} /> ); -}; +} export default ChildBounties; diff --git a/site/src/pages/Proposals/index.jsx b/site/src/pages/Proposals/index.jsx index aadeb5178..01fc16387 100644 --- a/site/src/pages/Proposals/index.jsx +++ b/site/src/pages/Proposals/index.jsx @@ -196,30 +196,44 @@ const Proposals = () => { const history = useHistory(); const { proposals, total, loading } = useListData(); - const doFetchProposal = useCallback(() => { - let filterData = getFilterData(); - if (isFailed) { - dispatch( - fetchFailedProposals( - tablePage - 1, - pageSize, - filterData, - sort && { sort }, - ), - ); - } else { - if (gov) { - filterData = { ...filterData, gov }; + const doFetchProposal = useCallback( + (options = {}) => { + let filterData = getFilterData(); + + const params = { + ...filterData, + }; + if (sort) { + params.sort = sort; } - dispatch( - fetchProposals(tablePage - 1, pageSize, filterData, sort && { sort }), - ); - } - }, [dispatch, tablePage, pageSize, getFilterData, sort, gov, isFailed]); + + if (isFailed) { + dispatch( + fetchFailedProposals(tablePage - 1, pageSize, params, options), + ); + } else { + if (gov) { + params.gov = gov; + } + dispatch(fetchProposals(tablePage - 1, pageSize, params, options)); + } + }, + [dispatch, tablePage, pageSize, getFilterData, sort, gov, isFailed], + ); + + useEffect(() => { + const searchParams = new URLSearchParams(history.location.search); + searchParams.delete("page"); + history.push({ search: searchParams.toString() }); + + setTablePage(DEFAULT_QUERY_PAGE); + }, [getFilterData]); useEffect(() => { - doFetchProposal(); + const controller = new AbortController(); + doFetchProposal({ signal: controller.signal }); return () => { + controller.abort(); dispatch(resetProposals()); }; }, [dispatch, doFetchProposal]); diff --git a/site/src/pages/Referenda/ReferendaTable.jsx b/site/src/pages/Referenda/ReferendaTable.jsx index 8764e0a10..94df029a7 100644 --- a/site/src/pages/Referenda/ReferendaTable.jsx +++ b/site/src/pages/Referenda/ReferendaTable.jsx @@ -87,11 +87,35 @@ export default function ReferendaTable() { } = useListFilter(); useEffect(() => { + const searchParams = new URLSearchParams(history.location.search); + searchParams.delete("page"); + history.push({ search: searchParams.toString() }); + + setPage(DEFAULT_QUERY_PAGE); + }, [getFilterData]); + + useEffect(() => { + const controller = new AbortController(); + const filterData = getFilterData(); + + const params = { + ...filterData, + }; + if (sort) { + params.sort = sort; + } + dispatch( - fetchApplicationList(page - 1, pageSize, filterData, sort && { sort }), + fetchApplicationList(page - 1, pageSize, params, { + signal: controller.signal, + }), ); - }, [dispatch, page, pageSize, sort, getFilterData]); + + return () => { + controller.abort(); + }; + }, [dispatch, page, pageSize, getFilterData, sort]); useEffect(() => { setDataList(applicationList?.items || []); diff --git a/site/src/pages/Tips/index.jsx b/site/src/pages/Tips/index.jsx index fda96b614..729169f90 100644 --- a/site/src/pages/Tips/index.jsx +++ b/site/src/pages/Tips/index.jsx @@ -84,17 +84,45 @@ const Tips = () => { const chain = useSelector(chainSelector); useEffect(() => { + const searchParams = new URLSearchParams(history.location.search); + searchParams.delete("page"); + history.push({ search: searchParams.toString() }); + + setTablePage(DEFAULT_QUERY_PAGE); + }, [getFilterData]); + + useEffect(() => { + const controller = new AbortController(); + const filterData = getFilterData(); - dispatch(fetchTips(tablePage - 1, pageSize, filterData, sort && { sort })); + const params = { + ...filterData, + }; + if (sort) { + params.sort = sort; + } + + dispatch( + fetchTips(tablePage - 1, pageSize, params, { + signal: controller.signal, + }), + ); return () => { + controller.abort(); dispatch(resetTips()); }; }, [dispatch, tablePage, pageSize, getFilterData, sort]); const refreshTips = useCallback(() => { const filterData = getFilterData(); - dispatch(fetchTips(tablePage - 1, pageSize, filterData, sort && { sort })); + const params = { + ...filterData, + }; + if (sort) { + params.sort = sort; + } + dispatch(fetchTips(tablePage - 1, pageSize, params)); }, [dispatch, tablePage, pageSize, getFilterData, sort]); const onFinalized = useWaitSyncBlock("Tips created", refreshTips); diff --git a/site/src/pages/Users/index.jsx b/site/src/pages/Users/index.jsx index 1ab0bcee9..5091eeb41 100644 --- a/site/src/pages/Users/index.jsx +++ b/site/src/pages/Users/index.jsx @@ -51,8 +51,27 @@ export default function Participants() { const { role, setRole, getFilterData } = useListFilter(); useEffect(() => { + const searchParams = new URLSearchParams(history.location.search); + searchParams.delete("page"); + history.push({ search: searchParams.toString() }); + + setTablePage(DEFAULT_QUERY_PAGE); + }, [getFilterData]); + + useEffect(() => { + const controller = new AbortController(); + const filterData = getFilterData(); - dispatch(fetchUsers(tablePage - 1, pageSize, filterData)); + + dispatch( + fetchUsers(tablePage - 1, pageSize, filterData, { + signal: controller.signal, + }), + ); + + return () => { + controller.abort(); + }; }, [dispatch, tablePage, pageSize, getFilterData]); const header = ( diff --git a/site/src/store/reducers/bountySlice.js b/site/src/store/reducers/bountySlice.js index e8ea37f69..ba10e00c4 100644 --- a/site/src/store/reducers/bountySlice.js +++ b/site/src/store/reducers/bountySlice.js @@ -64,17 +64,20 @@ export const { } = bountySlice.actions; export const fetchBounties = - (page = 0, pageSize = 30, filterData, sort) => + (page = 0, pageSize = 30, params, options = {}) => async (dispatch) => { dispatch(setLoading(true)); try { - const { result } = await api.fetch("/bounties", { - page, - pageSize, - ...filterData, - ...sort, - }); + const { result } = await api.fetch( + "/bounties", + { + page, + pageSize, + ...params, + }, + options, + ); dispatch(setBounties(result || {})); } finally { dispatch(setLoading(false)); @@ -122,17 +125,20 @@ export const fetchChildBountyDetail = (bountyIndex) => async (dispatch) => { } }; export const fetchChildBounties = - (page = 0, pageSize = 30, filterData, sort) => + (page = 0, pageSize = 30, params, options = {}) => async (dispatch) => { dispatch(setLoading(true)); try { - const { result } = await api.fetch("/child-bounties", { - page, - pageSize, - ...filterData, - ...sort, - }); + const { result } = await api.fetch( + "/child-bounties", + { + page, + pageSize, + ...params, + }, + options, + ); dispatch(setChildBounties(result || {})); } finally { dispatch(setLoading(false)); diff --git a/site/src/store/reducers/openGovApplicationsSlice.js b/site/src/store/reducers/openGovApplicationsSlice.js index d06761637..0b9a516a7 100644 --- a/site/src/store/reducers/openGovApplicationsSlice.js +++ b/site/src/store/reducers/openGovApplicationsSlice.js @@ -38,17 +38,20 @@ export const { } = openGovApplicationsSlice.actions; export const fetchApplicationList = - (page = 0, pageSize = 30, filterData = {}, sort = {}) => + (page = 0, pageSize = 30, params, options = {}) => async (dispatch) => { dispatch(setLoadingApplicationList(true)); try { - const { result } = await api.fetch("/referenda", { - page, - pageSize, - ...filterData, - ...sort, - }); + const { result } = await api.fetch( + "/referenda", + { + page, + pageSize, + ...params, + }, + options, + ); dispatch( setApplicationList( result || { diff --git a/site/src/store/reducers/proposalSlice.js b/site/src/store/reducers/proposalSlice.js index 21435e4f6..55d259c69 100644 --- a/site/src/store/reducers/proposalSlice.js +++ b/site/src/store/reducers/proposalSlice.js @@ -64,17 +64,20 @@ export const { } = proposalSlice.actions; export const fetchProposals = - (page = 0, pageSize = 30, filterData = {}, sort) => + (page = 0, pageSize = 30, params, options = {}) => async (dispatch) => { dispatch(setLoading(true)); try { - const { result } = await api.fetch("/proposals", { - page, - pageSize, - ...filterData, - ...sort, - }); + const { result } = await api.fetch( + "/proposals", + { + page, + pageSize, + ...params, + }, + options, + ); dispatch(setProposals(result || {})); } finally { dispatch(setLoading(false)); @@ -82,17 +85,20 @@ export const fetchProposals = }; export const fetchFailedProposals = - (page = 0, pageSize = 30, filterData = {}, sort) => + (page = 0, pageSize = 30, params, options = {}) => async (dispatch) => { dispatch(setFailedProposalsLoading(true)); try { - const { result } = await api.fetch("/proposals/failed", { - page, - pageSize, - ...filterData, - ...sort, - }); + const { result } = await api.fetch( + "/proposals/failed", + { + page, + pageSize, + ...params, + }, + options, + ); dispatch(setFailedProposals(result || {})); } finally { dispatch(setFailedProposalsLoading(false)); diff --git a/site/src/store/reducers/tipSlice.js b/site/src/store/reducers/tipSlice.js index 91151e040..3696d9cac 100644 --- a/site/src/store/reducers/tipSlice.js +++ b/site/src/store/reducers/tipSlice.js @@ -45,17 +45,20 @@ export const { } = tipSlice.actions; export const fetchTips = - (page = 0, pageSize = 30, filterData = {}, sort) => + (page = 0, pageSize = 30, params, options = {}) => async (dispatch) => { dispatch(setLoading(true)); try { - const { result } = await api.fetch("/tips", { - page, - pageSize, - ...filterData, - ...sort, - }); + const { result } = await api.fetch( + "/tips", + { + page, + pageSize, + ...params, + }, + options, + ); dispatch(setTips(result || {})); } finally { dispatch(setLoading(false)); @@ -99,7 +102,7 @@ export const tipListSelector = (state) => state.tips.tips; export const normalizedTipListSelector = createSelector( tipListSelector, (tips) => { - const items = tips.items.map(normalizeTip); + const items = tips?.items?.map(normalizeTip); return { ...tips, items, diff --git a/site/src/store/reducers/usersSlice.js b/site/src/store/reducers/usersSlice.js index 2ae911cf5..b7ba8aae7 100644 --- a/site/src/store/reducers/usersSlice.js +++ b/site/src/store/reducers/usersSlice.js @@ -25,20 +25,26 @@ const usersSlice = createSlice({ export const { setLoading, setUsers } = usersSlice.actions; -export const fetchUsers = (page, pageSize, filterData) => async (dispatch) => { - dispatch(setLoading(true)); +export const fetchUsers = + (page, pageSize, params, options = {}) => + async (dispatch) => { + dispatch(setLoading(true)); - try { - const { result } = await api.fetch("/participants", { - page, - pageSize, - ...filterData, - }); - dispatch(setUsers(result || {})); - } finally { - dispatch(setLoading(false)); - } -}; + try { + const { result } = await api.fetch( + "/participants", + { + page, + pageSize, + ...params, + }, + options, + ); + dispatch(setUsers(result || {})); + } finally { + dispatch(setLoading(false)); + } + }; export const usersSelector = (state) => state.users.users; export const loadingSelector = (state) => state.users.loading;