diff --git a/CHANGELOG.md b/CHANGELOG.md index c63d73e5..bd73931a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Channels table pagination error handling: Fixed "Invalid page" error notifications that appeared when filters reduced the result set while on a page beyond the new total. The API layer now automatically detects invalid page errors, resets pagination to page 1, and retries the request transparently. (Fixes #864) - Fixed long IP addresses overlapping adjacent columns in stream connection card by adding truncation with tooltips displaying the full address. (Fixes #712) - Fixed nginx startup failure due to group name mismatch in non-container deployments - Thanks [@s0len](https://github.com/s0len) (Fixes #877) - Updated streamlink from 8.1.0 to 8.1.2 to fix YouTube live stream playback issues and improve Pluto TV ad detection (Fixes #869) diff --git a/frontend/src/api.js b/frontend/src/api.js index 4b728622..3df0b475 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -198,6 +198,31 @@ export default class API { return response; } catch (e) { + // Handle invalid page error by resetting to page 1 and retrying + if (e.body?.detail === 'Invalid page.') { + const currentPagination = useChannelsTableStore.getState().pagination; + + // Only retry if we're not already on page 1 + if (currentPagination.pageIndex > 0) { + // Reset to page 1 + useChannelsTableStore.getState().setPagination({ + ...currentPagination, + pageIndex: 0, + }); + + // Update params to page 1 and retry + const newParams = new URLSearchParams(params); + newParams.set('page', '1'); + + const response = await request( + `${host}/api/channels/channels/?${newParams.toString()}` + ); + + useChannelsTableStore.getState().queryChannels(response, newParams); + return response; + } + } + errorNotification('Failed to fetch channels', e); } } @@ -218,6 +243,39 @@ export default class API { return response; } catch (e) { + // Handle invalid page error by resetting to page 1 and retrying + if (e.body?.detail === 'Invalid page.') { + const currentPagination = useChannelsTableStore.getState().pagination; + + // Only retry if we're not already on page 1 + if (currentPagination.pageIndex > 0) { + // Reset to page 1 + useChannelsTableStore.getState().setPagination({ + ...currentPagination, + pageIndex: 0, + }); + + // Update params to page 1 and retry + const newParams = new URLSearchParams(API.lastQueryParams); + newParams.set('page', '1'); + API.lastQueryParams = newParams; + + const [response, ids] = await Promise.all([ + request( + `${host}/api/channels/channels/?${newParams.toString()}` + ), + API.getAllChannelIds(newParams), + ]); + + useChannelsTableStore + .getState() + .queryChannels(response, newParams); + useChannelsTableStore.getState().setAllQueryIds(ids); + + return response; + } + } + errorNotification('Failed to fetch channels', e); } } diff --git a/frontend/src/components/tables/ChannelsTable.jsx b/frontend/src/components/tables/ChannelsTable.jsx index 78dd4f4c..bcacfe98 100644 --- a/frontend/src/components/tables/ChannelsTable.jsx +++ b/frontend/src/components/tables/ChannelsTable.jsx @@ -421,25 +421,32 @@ const ChannelsTable = ({ onReady }) => { } }); - const [results, ids] = await Promise.all([ - await API.queryChannels(params), - await API.getAllChannelIds(params), - ]); + try { + const [results, ids] = await Promise.all([ + await API.queryChannels(params), + await API.getAllChannelIds(params), + ]); - setIsLoading(false); - hasFetchedData.current = true; + setIsLoading(false); + hasFetchedData.current = true; - setTablePrefs((prev) => ({ - ...prev, - pageSize: pagination.pageSize, - })); - setAllRowIds(ids); + setTablePrefs((prev) => ({ + ...prev, + pageSize: pagination.pageSize, + })); + setAllRowIds(ids); - // Signal ready after first successful data fetch AND EPG data is loaded - // This prevents the EPG column from showing "Not Assigned" while EPG data is still loading - if (!hasSignaledReady.current && onReady && tvgsLoaded) { - hasSignaledReady.current = true; - onReady(); + // Signal ready after first successful data fetch AND EPG data is loaded + // This prevents the EPG column from showing "Not Assigned" while EPG data is still loading + if (!hasSignaledReady.current && onReady && tvgsLoaded) { + hasSignaledReady.current = true; + onReady(); + } + } catch (error) { + setIsLoading(false); + // API layer handles "Invalid page" errors by resetting and retrying + // Just re-throw to show notification for actual errors + throw error; } }, [ pagination,