From 8348118ff297f59a1d442828f5204ef1cdc1f386 Mon Sep 17 00:00:00 2001 From: LASER-Yi Date: Tue, 17 Aug 2021 22:27:33 +0800 Subject: Add pagination support to history page --- bazarr/api.py | 6 +++-- frontend/src/@redux/actions/movie.ts | 12 +++++---- frontend/src/@redux/actions/series.ts | 12 +++++---- frontend/src/@redux/hooks/movies.ts | 3 --- frontend/src/@redux/hooks/series.ts | 3 --- frontend/src/@redux/reducers/movie.ts | 10 ++++---- frontend/src/@redux/reducers/series.ts | 10 ++++---- frontend/src/@socketio/reducer.ts | 6 ++--- frontend/src/@types/api.d.ts | 1 + frontend/src/History/Movies/index.tsx | 6 ++++- frontend/src/History/Series/index.tsx | 6 ++++- frontend/src/History/generic/index.tsx | 34 ++++++++++++------------- frontend/src/apis/episodes.ts | 24 +++++++++-------- frontend/src/apis/movies.ts | 26 ++++++++++--------- frontend/src/components/modals/HistoryModal.tsx | 12 ++++----- 15 files changed, 92 insertions(+), 79 deletions(-) diff --git a/bazarr/api.py b/bazarr/api.py index 5e56f61f7..29a4c8486 100644 --- a/bazarr/api.py +++ b/bazarr/api.py @@ -1419,7 +1419,8 @@ class EpisodesHistory(Resource): if episodeid: query_conditions.append((TableEpisodes.sonarrEpisodeId == episodeid)) query_condition = reduce(operator.and_, query_conditions) - episode_history = TableHistory.select(TableShows.title.alias('seriesTitle'), + episode_history = TableHistory.select(TableHistory.id, + TableShows.title.alias('seriesTitle'), TableEpisodes.monitored, TableEpisodes.season.concat('x').concat(TableEpisodes.episode).alias('episode_number'), TableEpisodes.title.alias('episodeTitle'), @@ -1535,7 +1536,8 @@ class MoviesHistory(Resource): query_conditions.append((TableMovies.radarrId == radarrid)) query_condition = reduce(operator.and_, query_conditions) - movie_history = TableHistoryMovie.select(TableHistoryMovie.action, + movie_history = TableHistoryMovie.select(TableHistoryMovie.id, + TableHistoryMovie.action, TableMovies.title, TableHistoryMovie.timestamp, TableHistoryMovie.description, diff --git a/frontend/src/@redux/actions/movie.ts b/frontend/src/@redux/actions/movie.ts index 6deafc770..af9aa4c1b 100644 --- a/frontend/src/@redux/actions/movie.ts +++ b/frontend/src/@redux/actions/movie.ts @@ -55,15 +55,17 @@ export const movieUpdateWantedByRange = createAsyncThunk( } ); -export const movieUpdateHistory = createAsyncThunk( - "movies/history/update", - async () => { - const response = await MoviesApi.history(); +export const movieUpdateHistoryByRange = createAsyncThunk( + "movies/history/update/range", + async (params: Parameter.Range) => { + const response = await MoviesApi.history(params); return response; } ); -export const movieMarkHistoryDirty = createAction("movies/history/mark_dirty"); +export const movieMarkHistoryDirty = createAction( + "movies/history/mark_dirty" +); export const movieUpdateBlacklist = createAsyncThunk( "movies/blacklist/update", diff --git a/frontend/src/@redux/actions/series.ts b/frontend/src/@redux/actions/series.ts index 41e79408a..a9ab035a6 100644 --- a/frontend/src/@redux/actions/series.ts +++ b/frontend/src/@redux/actions/series.ts @@ -77,15 +77,17 @@ export const episodeUpdateById = createAsyncThunk( } ); -export const episodesUpdateHistory = createAsyncThunk( - "episodes/history/update", - async () => { - const response = await EpisodesApi.history(); +export const episodesUpdateHistoryByRange = createAsyncThunk( + "episodes/history/update/range", + async (param: Parameter.Range) => { + const response = await EpisodesApi.history(param); return response; } ); -export const episodesMarkHistoryDirty = createAction("episodes/history/update"); +export const episodesMarkHistoryDirty = createAction( + "episodes/history/update" +); export const episodesUpdateBlacklist = createAsyncThunk( "episodes/blacklist/update", diff --git a/frontend/src/@redux/hooks/movies.ts b/frontend/src/@redux/hooks/movies.ts index 0a027818e..df8a82272 100644 --- a/frontend/src/@redux/hooks/movies.ts +++ b/frontend/src/@redux/hooks/movies.ts @@ -3,7 +3,6 @@ import { useEntityItemById, useEntityToList } from "../../utilites"; import { movieUpdateBlacklist, movieUpdateById, - movieUpdateHistory, movieUpdateWantedById, } from "../actions"; import { useAutoDirtyUpdate, useAutoUpdate } from "./async"; @@ -62,9 +61,7 @@ export function useBlacklistMovies() { } export function useMoviesHistory() { - const update = useReduxAction(movieUpdateHistory); const items = useReduxStore((s) => s.movies.historyList); - useAutoUpdate(items, update); return items; } diff --git a/frontend/src/@redux/hooks/series.ts b/frontend/src/@redux/hooks/series.ts index 0de8df2cc..d37b7eb32 100644 --- a/frontend/src/@redux/hooks/series.ts +++ b/frontend/src/@redux/hooks/series.ts @@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo } from "react"; import { useEntityItemById, useEntityToList } from "../../utilites"; import { episodesUpdateBlacklist, - episodesUpdateHistory, episodeUpdateById, episodeUpdateBySeriesId, seriesUpdateById, @@ -94,9 +93,7 @@ export function useBlacklistSeries() { } export function useSeriesHistory() { - const update = useReduxAction(episodesUpdateHistory); const items = useReduxStore((s) => s.series.historyList); - useAutoUpdate(items, update); return items; } diff --git a/frontend/src/@redux/reducers/movie.ts b/frontend/src/@redux/reducers/movie.ts index 7de902801..78ad5e86c 100644 --- a/frontend/src/@redux/reducers/movie.ts +++ b/frontend/src/@redux/reducers/movie.ts @@ -10,7 +10,7 @@ import { movieUpdateBlacklist, movieUpdateById, movieUpdateByRange, - movieUpdateHistory, + movieUpdateHistoryByRange, movieUpdateWantedById, movieUpdateWantedByRange, } from "../actions"; @@ -23,14 +23,14 @@ import { interface Movie { movieList: Async.Entity; wantedMovieList: Async.Entity; - historyList: Async.Item; + historyList: Async.Entity; blacklist: Async.Item; } const defaultMovie: Movie = { movieList: AsyncUtility.getDefaultEntity("radarrId"), wantedMovieList: AsyncUtility.getDefaultEntity("radarrId"), - historyList: AsyncUtility.getDefaultItem(), + historyList: AsyncUtility.getDefaultEntity("id"), blacklist: AsyncUtility.getDefaultItem(), }; @@ -50,8 +50,8 @@ const reducer = createReducer(defaultMovie, (builder) => { dirty: movieMarkWantedDirtyById, }); - createAsyncItemReducer(builder, (s) => s.historyList, { - all: movieUpdateHistory, + createAsyncEntityReducer(builder, (s) => s.historyList, { + range: movieUpdateHistoryByRange, dirty: movieMarkHistoryDirty, }); diff --git a/frontend/src/@redux/reducers/series.ts b/frontend/src/@redux/reducers/series.ts index 1facde9f1..1ce6dbc25 100644 --- a/frontend/src/@redux/reducers/series.ts +++ b/frontend/src/@redux/reducers/series.ts @@ -5,7 +5,7 @@ import { episodesMarkHistoryDirty, episodesRemoveById, episodesUpdateBlacklist, - episodesUpdateHistory, + episodesUpdateHistoryByRange, episodeUpdateById, episodeUpdateBySeriesId, seriesMarkDirtyById, @@ -29,7 +29,7 @@ interface Series { seriesList: Async.Entity; wantedEpisodesList: Async.Entity; episodeList: Async.List; - historyList: Async.Item; + historyList: Async.Entity; blacklist: Async.Item; } @@ -37,7 +37,7 @@ const defaultSeries: Series = { seriesList: AsyncUtility.getDefaultEntity("sonarrSeriesId"), wantedEpisodesList: AsyncUtility.getDefaultEntity("sonarrEpisodeId"), episodeList: AsyncUtility.getDefaultList("sonarrEpisodeId"), - historyList: AsyncUtility.getDefaultItem(), + historyList: AsyncUtility.getDefaultEntity("id"), blacklist: AsyncUtility.getDefaultItem(), }; @@ -72,8 +72,8 @@ const reducer = createReducer(defaultSeries, (builder) => { dirty: seriesMarkWantedDirtyById, }); - createAsyncItemReducer(builder, (s) => s.historyList, { - all: episodesUpdateHistory, + createAsyncEntityReducer(builder, (s) => s.historyList, { + range: episodesUpdateHistoryByRange, dirty: episodesMarkHistoryDirty, }); diff --git a/frontend/src/@socketio/reducer.ts b/frontend/src/@socketio/reducer.ts index 8ed251a97..153915939 100644 --- a/frontend/src/@socketio/reducer.ts +++ b/frontend/src/@socketio/reducer.ts @@ -2,11 +2,9 @@ import { ActionCreator } from "@reduxjs/toolkit"; import { episodesMarkBlacklistDirty, episodesMarkDirtyById, - episodesMarkHistoryDirty, episodesRemoveById, movieMarkBlacklistDirty, movieMarkDirtyById, - movieMarkHistoryDirty, movieMarkWantedDirtyById, movieRemoveById, movieRemoveWantedById, @@ -130,7 +128,7 @@ export function createDefaultReducer(): SocketIO.Reducer[] { }, { key: "movie-history", - any: bindReduxAction(movieMarkHistoryDirty), + // any: bindReduxAction(movieMarkHistoryDirty), }, { key: "movie-blacklist", @@ -138,7 +136,7 @@ export function createDefaultReducer(): SocketIO.Reducer[] { }, { key: "episode-history", - any: bindReduxAction(episodesMarkHistoryDirty), + // any: bindReduxAction(episodesMarkHistoryDirty), }, { key: "episode-blacklist", diff --git a/frontend/src/@types/api.d.ts b/frontend/src/@types/api.d.ts index bcc5abed9..043753879 100644 --- a/frontend/src/@types/api.d.ts +++ b/frontend/src/@types/api.d.ts @@ -195,6 +195,7 @@ declare namespace History { TagType & MonitoredType & Partial & { + id: number; action: number; blacklisted: boolean; score?: string; diff --git a/frontend/src/History/Movies/index.tsx b/frontend/src/History/Movies/index.tsx index 7669bf30c..9a387ecae 100644 --- a/frontend/src/History/Movies/index.tsx +++ b/frontend/src/History/Movies/index.tsx @@ -4,7 +4,9 @@ import React, { FunctionComponent, useMemo } from "react"; import { Badge, OverlayTrigger, Popover } from "react-bootstrap"; import { Link } from "react-router-dom"; import { Column } from "react-table"; +import { movieUpdateHistoryByRange } from "../../@redux/actions"; import { useMoviesHistory } from "../../@redux/hooks"; +import { useReduxAction } from "../../@redux/hooks/base"; import { MoviesApi } from "../../apis"; import { HistoryIcon, LanguageText, TextPopover } from "../../components"; import { BlacklistButton } from "../../generic/blacklist"; @@ -14,6 +16,7 @@ interface Props {} const MoviesHistoryView: FunctionComponent = () => { const movies = useMoviesHistory(); + const loader = useReduxAction(movieUpdateHistoryByRange); const columns: Column[] = useMemo[]>( () => [ @@ -128,7 +131,8 @@ const MoviesHistoryView: FunctionComponent = () => { []} + loader={loader} + columns={columns} > ); }; diff --git a/frontend/src/History/Series/index.tsx b/frontend/src/History/Series/index.tsx index 6c82d84a1..e96c3e689 100644 --- a/frontend/src/History/Series/index.tsx +++ b/frontend/src/History/Series/index.tsx @@ -4,7 +4,9 @@ import React, { FunctionComponent, useMemo } from "react"; import { Badge, OverlayTrigger, Popover } from "react-bootstrap"; import { Link } from "react-router-dom"; import { Column } from "react-table"; +import { episodesUpdateHistoryByRange } from "../../@redux/actions"; import { useSeriesHistory } from "../../@redux/hooks"; +import { useReduxAction } from "../../@redux/hooks/base"; import { EpisodesApi } from "../../apis"; import { HistoryIcon, LanguageText, TextPopover } from "../../components"; import { BlacklistButton } from "../../generic/blacklist"; @@ -14,6 +16,7 @@ interface Props {} const SeriesHistoryView: FunctionComponent = () => { const series = useSeriesHistory(); + const loader = useReduxAction(episodesUpdateHistoryByRange); const columns: Column[] = useMemo[]>( () => [ @@ -137,7 +140,8 @@ const SeriesHistoryView: FunctionComponent = () => { []} + loader={loader} + columns={columns} > ); }; diff --git a/frontend/src/History/generic/index.tsx b/frontend/src/History/generic/index.tsx index 58dbe5acd..0775c6fae 100644 --- a/frontend/src/History/generic/index.tsx +++ b/frontend/src/History/generic/index.tsx @@ -1,21 +1,23 @@ import { capitalize } from "lodash"; -import React, { FunctionComponent } from "react"; +import React from "react"; import { Container, Row } from "react-bootstrap"; import { Helmet } from "react-helmet"; import { Column } from "react-table"; -import { AsyncOverlay, PageTable } from "../../components"; +import { AsyncPageTable } from "../../components"; -interface Props { +interface Props { type: "movies" | "series"; - state: Readonly>; - columns: Column[]; + state: Readonly>; + loader: (param: Parameter.Range) => void; + columns: Column[]; } -const HistoryGenericView: FunctionComponent = ({ +function HistoryGenericView({ state, + loader, columns, type, -}) => { +}: Props) { const typeName = capitalize(type); return ( @@ -23,18 +25,16 @@ const HistoryGenericView: FunctionComponent = ({ {typeName} History - Bazarr - - {({ content }) => ( - - )} - + ); -}; +} export default HistoryGenericView; diff --git a/frontend/src/apis/episodes.ts b/frontend/src/apis/episodes.ts index 4b8eefa0e..e369918d5 100644 --- a/frontend/src/apis/episodes.ts +++ b/frontend/src/apis/episodes.ts @@ -53,16 +53,20 @@ class EpisodeApi extends BaseApi { }); } - async history(episodeid?: number): Promise { - return new Promise((resolve, reject) => { - this.get>("/history", { episodeid }) - .then((result) => { - resolve(result.data.data); - }) - .catch((reason) => { - reject(reason); - }); - }); + async history(params: Parameter.Range) { + const response = await this.get>( + "/history", + params + ); + return response.data; + } + + async historyBy(episodeid: number) { + const response = await this.get>( + "/history", + { episodeid } + ); + return response.data; } async downloadSubtitles( diff --git a/frontend/src/apis/movies.ts b/frontend/src/apis/movies.ts index ac6bad5ae..aa978a77e 100644 --- a/frontend/src/apis/movies.ts +++ b/frontend/src/apis/movies.ts @@ -93,18 +93,20 @@ class MovieApi extends BaseApi { }); } - async history(id?: number): Promise { - return new Promise((resolve, reject) => { - this.get>("/history", { - radarrid: id, - }) - .then((result) => { - resolve(result.data.data); - }) - .catch((reason) => { - reject(reason); - }); - }); + async history(params: Parameter.Range) { + const response = await this.get>( + "/history", + params + ); + return response.data; + } + + async historyBy(radarrid: number) { + const response = await this.get>( + "/history", + { radarrid } + ); + return response.data; } async action(action: FormType.MoviesAction) { diff --git a/frontend/src/components/modals/HistoryModal.tsx b/frontend/src/components/modals/HistoryModal.tsx index 79f14b9ff..9e19d8295 100644 --- a/frontend/src/components/modals/HistoryModal.tsx +++ b/frontend/src/components/modals/HistoryModal.tsx @@ -14,8 +14,8 @@ export const MovieHistoryModal: FunctionComponent = (props) => { const movie = useModalPayload(modal.modalKey); const [history, updateHistory] = useAsyncRequest( - MoviesApi.history.bind(MoviesApi), - [] + MoviesApi.historyBy.bind(MoviesApi), + { data: [], total: 0 } ); const update = useCallback(() => { @@ -98,7 +98,7 @@ export const MovieHistoryModal: FunctionComponent = (props) => { )} @@ -114,8 +114,8 @@ export const EpisodeHistoryModal: FunctionComponent< const episode = useModalPayload(props.modalKey); const [history, updateHistory] = useAsyncRequest( - EpisodesApi.history.bind(EpisodesApi), - [] + EpisodesApi.historyBy.bind(EpisodesApi), + { data: [], total: 0 } ); const update = useCallback(() => { @@ -199,7 +199,7 @@ export const EpisodeHistoryModal: FunctionComponent< )} -- cgit v1.2.3