move pagination state entirely to URL params

don't duplicate it state variables
This commit is contained in:
Min RK
2024-03-06 00:23:38 +01:00
parent cabc05f7dd
commit fd78a03280
4 changed files with 91 additions and 156 deletions

View File

@@ -1,55 +1,20 @@
export const initialState = { export const initialState = {
user_data: undefined, user_data: undefined,
user_page: { offset: 0, limit: window.api_page_limit || 100 }, user_page: undefined,
name_filter: "",
groups_data: undefined, groups_data: undefined,
groups_page: { offset: 0, limit: window.api_page_limit || 100 }, groups_page: undefined,
limit: window.api_page_limit || 100, limit: window.api_page_limit || 100,
}; };
export const reducers = (state = initialState, action) => { export const reducers = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
// Updates the client user model data and stores the page // Updates the client user model data and stores the page
case "USER_OFFSET":
return Object.assign({}, state, {
user_page: Object.assign({}, state.user_page, {
offset: action.value.offset,
}),
});
case "USER_LIMIT":
return Object.assign({}, state, {
user_page: Object.assign({}, state.user_page, {
limit: action.value.limit,
}),
});
case "USER_NAME_FILTER":
// set offset to 0 if name filter changed,
// otherwise leave it alone
const newOffset =
action.value.name_filter !== state.name_filter ? 0 : state.name_filter;
return Object.assign({}, state, {
user_page: Object.assign({}, state.user_page, {
offset: newOffset,
}),
name_filter: action.value.name_filter,
});
case "USER_PAGE": case "USER_PAGE":
return Object.assign({}, state, { return Object.assign({}, state, {
user_page: action.value.page, user_page: action.value.page,
user_data: action.value.data, user_data: action.value.data,
}); });
// Updates the client group user model data and stores the page
case "GROUPS_OFFSET":
return Object.assign({}, state, {
groups_page: Object.assign({}, state.groups_page, {
offset: action.value.offset,
}),
});
case "GROUPS_PAGE": case "GROUPS_PAGE":
return Object.assign({}, state, { return Object.assign({}, state, {
groups_page: action.value.page, groups_page: action.value.page,

View File

@@ -1,7 +1,6 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { debounce } from "lodash";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useSearchParams } from "react-router-dom-v5-compat"; import { useSearchParams } from "react-router-dom-v5-compat";
@@ -12,49 +11,15 @@ const Groups = (props) => {
groups_page = useSelector((state) => state.groups_page), groups_page = useSelector((state) => state.groups_page),
dispatch = useDispatch(); dispatch = useDispatch();
let [searchParams, setSearchParams] = useSearchParams(); const { setOffset, offset, setLimit, handleLimit, limit, setPagination } =
usePaginationParams();
var offset = parseInt(searchParams.get("offset", "0")) || 0;
var limit = parseInt(searchParams.get("limit", "0")) || window.api_page_limit;
const setOffset = (offset) => {
console.log("setting offset", offset);
if (offset < 0) {
offset = 0;
}
setSearchParams((params) => {
params.set("offset", offset);
return params;
});
dispatch({
type: "GROUPS_OFFSET",
value: {
offset: offset,
},
});
};
const setLimit = (newLimit) => {
if (newLimit < 1) {
newLimit = 10;
}
setSearchParams((params) => {
params.set("limit", newLimit);
return params;
});
dispatch({
type: "GROUP_LIMIT",
value: {
limit: newLimit,
},
});
};
var total = groups_page ? groups_page.total : undefined; var total = groups_page ? groups_page.total : undefined;
var { updateGroups, history } = props; var { updateGroups, history } = props;
const dispatchPageUpdate = (data, page) => { const dispatchPageUpdate = (data, page) => {
setPagination(page);
dispatch({ dispatch({
type: "GROUPS_PAGE", type: "GROUPS_PAGE",
value: { value: {
@@ -74,10 +39,6 @@ const Groups = (props) => {
return <div data-testid="no-show"></div>; return <div data-testid="no-show"></div>;
} }
const handleLimit = debounce(async (event) => {
setLimit(event.target.value);
}, 300);
return ( return (
<div className="container" data-testid="container"> <div className="container" data-testid="container">
<div className="row"> <div className="row">

View File

@@ -22,6 +22,7 @@ import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
import "./server-dashboard.css"; import "./server-dashboard.css";
import { timeSince } from "../../util/timeSince"; import { timeSince } from "../../util/timeSince";
import { usePaginationParams } from "../../util/paginationParams";
import PaginationFooter from "../PaginationFooter/PaginationFooter"; import PaginationFooter from "../PaginationFooter/PaginationFooter";
const RowListItem = ({ text }) => ( const RowListItem = ({ text }) => (
@@ -32,10 +33,10 @@ RowListItem.propTypes = {
}; };
const ServerDashboard = (props) => { const ServerDashboard = (props) => {
let base_url = window.base_url || "/"; const base_url = window.base_url || "/";
let [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
// sort methods // sort methods
var usernameDesc = (e) => e.sort((a, b) => (a.name > b.name ? 1 : -1)), const usernameDesc = (e) => e.sort((a, b) => (a.name > b.name ? 1 : -1)),
usernameAsc = (e) => e.sort((a, b) => (a.name < b.name ? 1 : -1)), usernameAsc = (e) => e.sort((a, b) => (a.name < b.name ? 1 : -1)),
adminDesc = (e) => e.sort((a) => (a.admin ? -1 : 1)), adminDesc = (e) => e.sort((a) => (a.admin ? -1 : 1)),
adminAsc = (e) => e.sort((a) => (a.admin ? 1 : -1)), adminAsc = (e) => e.sort((a) => (a.admin ? 1 : -1)),
@@ -50,20 +51,19 @@ const ServerDashboard = (props) => {
runningAsc = (e) => e.sort((a) => (a.server == null ? -1 : 1)), runningAsc = (e) => e.sort((a) => (a.server == null ? -1 : 1)),
runningDesc = (e) => e.sort((a) => (a.server == null ? 1 : -1)); runningDesc = (e) => e.sort((a) => (a.server == null ? 1 : -1));
var [errorAlert, setErrorAlert] = useState(null); const [errorAlert, setErrorAlert] = useState(null);
var [sortMethod, setSortMethod] = useState(null); const [sortMethod, setSortMethod] = useState(null);
var [collapseStates, setCollapseStates] = useState({}); const [collapseStates, setCollapseStates] = useState({});
var user_data = useSelector((state) => state.user_data), const user_data = useSelector((state) => state.user_data);
user_page = useSelector((state) => state.user_page), const user_page = useSelector((state) => state.user_page);
name_filter = useSelector((state) => state.name_filter);
// get offset, limit, name filter from URL const { setOffset, offset, setLimit, handleLimit, limit, setPagination } =
var offset = parseInt(searchParams.get("offset", "0")) || 0; usePaginationParams();
var limit = parseInt(searchParams.get("limit", "0")) || window.api_page_limit;
var searchNameFilter = searchParams.get("name_filter");
var total = user_page ? user_page.total : undefined; const name_filter = searchParams.get("name_filter");
const total = user_page ? user_page.total : undefined;
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -79,6 +79,7 @@ const ServerDashboard = (props) => {
} = props; } = props;
const dispatchPageUpdate = (data, page) => { const dispatchPageUpdate = (data, page) => {
setPagination(page);
dispatch({ dispatch({
type: "USER_PAGE", type: "USER_PAGE",
value: { value: {
@@ -88,49 +89,15 @@ const ServerDashboard = (props) => {
}); });
}; };
const setOffset = (newOffset) => {
if (newOffset < 0) {
newOffset = 0;
}
setSearchParams((params) => {
params.set("offset", newOffset);
return params;
});
dispatch({
type: "USER_OFFSET",
value: {
offset: newOffset,
},
});
};
const setLimit = (newLimit) => {
if (newLimit < 1) {
newLimit = 10;
}
setSearchParams((params) => {
params.set("limit", newLimit);
return params;
});
dispatch({
type: "USER_LIMIT",
value: {
limit: newLimit,
},
});
};
const setNameFilter = (name_filter) => { const setNameFilter = (name_filter) => {
setSearchParams((params) => { setSearchParams((params) => {
if (name_filter) {
params.set("name_filter", name_filter); params.set("name_filter", name_filter);
} else {
params.delete("name_filter");
}
return params; return params;
}); });
dispatch({
type: "USER_NAME_FILTER",
value: {
name_filter: name_filter,
},
});
}; };
useEffect(() => { useEffect(() => {
@@ -139,11 +106,6 @@ const ServerDashboard = (props) => {
.catch((err) => setErrorAlert("Failed to update user list.")); .catch((err) => setErrorAlert("Failed to update user list."));
}, [offset, limit, name_filter]); }, [offset, limit, name_filter]);
if (searchNameFilter && name_filter != searchNameFilter) {
// get name_filter from URL
setNameFilter(searchNameFilter);
}
if (!user_data || !user_page) { if (!user_data || !user_page) {
return <div data-testid="no-show"></div>; return <div data-testid="no-show"></div>;
} }
@@ -154,10 +116,6 @@ const ServerDashboard = (props) => {
setNameFilter(event.target.value); setNameFilter(event.target.value);
}, 300); }, 300);
const handleLimit = debounce(async (event) => {
setLimit(event.target.value);
}, 300);
if (sortMethod != null) { if (sortMethod != null) {
user_data = sortMethod(user_data); user_data = sortMethod(user_data);
} }
@@ -175,11 +133,7 @@ const ServerDashboard = (props) => {
if (res.status < 300) { if (res.status < 300) {
updateUsers(...slice) updateUsers(...slice)
.then((data) => { .then((data) => {
dispatchPageUpdate( dispatchPageUpdate(data.items, data._pagination);
data.items,
data._pagination,
name_filter,
);
}) })
.catch(() => { .catch(() => {
setIsDisabled(false); setIsDisabled(false);
@@ -537,11 +491,7 @@ const ServerDashboard = (props) => {
.then((res) => { .then((res) => {
updateUsers(...slice) updateUsers(...slice)
.then((data) => { .then((data) => {
dispatchPageUpdate( dispatchPageUpdate(data.items, data._pagination);
data.items,
data._pagination,
name_filter,
);
}) })
.catch(() => .catch(() =>
setErrorAlert(`Failed to update users list.`), setErrorAlert(`Failed to update users list.`),
@@ -577,11 +527,7 @@ const ServerDashboard = (props) => {
.then((res) => { .then((res) => {
updateUsers(...slice) updateUsers(...slice)
.then((data) => { .then((data) => {
dispatchPageUpdate( dispatchPageUpdate(data.items, data._pagination);
data.items,
data._pagination,
name_filter,
);
}) })
.catch(() => .catch(() =>
setErrorAlert(`Failed to update users list.`), setErrorAlert(`Failed to update users list.`),

View File

@@ -0,0 +1,63 @@
import { debounce } from "lodash";
import { useSearchParams } from "react-router-dom-v5-compat";
export const usePaginationParams = () => {
// get offset, limit, name filter from URL
const [searchParams, setSearchParams] = useSearchParams();
const offset = parseInt(searchParams.get("offset", "0")) || 0;
const limit =
parseInt(searchParams.get("limit", "0")) || window.api_page_limit;
const _setOffset = (params, offset) => {
if (offset < 0) offset = 0;
if (offset === 0) {
params.delete("offset");
} else {
params.set("offset", offset);
}
};
const _setLimit = (params, limit) => {
if (limit < 10) limit = 10;
if (limit === window.api_page_limit) {
params.delete("limit");
} else {
params.set("limit", limit);
}
};
const setPagination = (pagination) => {
// update pagination in one
setSearchParams((params) => {
_setOffset(params, pagination.offset);
_setLimit(params, pagination.limit);
return params;
});
};
const setOffset = (offset) => {
if (offset < 0) offset = 0;
setSearchParams((params) => {
_setOffset(params, offset);
return params;
});
};
const setLimit = (limit) => {
setSearchParams((params) => {
_setLimit(params, limit);
return params;
});
};
const handleLimit = debounce(async (event) => {
setLimit(event.target.value);
}, 300);
return {
offset,
setOffset,
limit,
setLimit,
handleLimit,
setPagination,
};
};