mirror of
https://github.com/morpheus65535/bazarr
synced 2025-03-15 00:18:48 +00:00
Add pagination support to history page
This commit is contained in:
parent
156cf1882c
commit
8348118ff2
15 changed files with 92 additions and 79 deletions
|
@ -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,
|
||||
|
|
|
@ -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<number[]>(
|
||||
"movies/history/mark_dirty"
|
||||
);
|
||||
|
||||
export const movieUpdateBlacklist = createAsyncThunk(
|
||||
"movies/blacklist/update",
|
||||
|
|
|
@ -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<number[]>(
|
||||
"episodes/history/update"
|
||||
);
|
||||
|
||||
export const episodesUpdateBlacklist = createAsyncThunk(
|
||||
"episodes/blacklist/update",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
movieUpdateBlacklist,
|
||||
movieUpdateById,
|
||||
movieUpdateByRange,
|
||||
movieUpdateHistory,
|
||||
movieUpdateHistoryByRange,
|
||||
movieUpdateWantedById,
|
||||
movieUpdateWantedByRange,
|
||||
} from "../actions";
|
||||
|
@ -23,14 +23,14 @@ import {
|
|||
interface Movie {
|
||||
movieList: Async.Entity<Item.Movie>;
|
||||
wantedMovieList: Async.Entity<Wanted.Movie>;
|
||||
historyList: Async.Item<History.Movie[]>;
|
||||
historyList: Async.Entity<History.Movie>;
|
||||
blacklist: Async.Item<Blacklist.Movie[]>;
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
episodesMarkHistoryDirty,
|
||||
episodesRemoveById,
|
||||
episodesUpdateBlacklist,
|
||||
episodesUpdateHistory,
|
||||
episodesUpdateHistoryByRange,
|
||||
episodeUpdateById,
|
||||
episodeUpdateBySeriesId,
|
||||
seriesMarkDirtyById,
|
||||
|
@ -29,7 +29,7 @@ interface Series {
|
|||
seriesList: Async.Entity<Item.Series>;
|
||||
wantedEpisodesList: Async.Entity<Wanted.Episode>;
|
||||
episodeList: Async.List<Item.Episode>;
|
||||
historyList: Async.Item<History.Episode[]>;
|
||||
historyList: Async.Entity<History.Episode>;
|
||||
blacklist: Async.Item<Blacklist.Episode[]>;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
1
frontend/src/@types/api.d.ts
vendored
1
frontend/src/@types/api.d.ts
vendored
|
@ -195,6 +195,7 @@ declare namespace History {
|
|||
TagType &
|
||||
MonitoredType &
|
||||
Partial<ItemHistoryType> & {
|
||||
id: number;
|
||||
action: number;
|
||||
blacklisted: boolean;
|
||||
score?: string;
|
||||
|
|
|
@ -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<Props> = () => {
|
||||
const movies = useMoviesHistory();
|
||||
const loader = useReduxAction(movieUpdateHistoryByRange);
|
||||
|
||||
const columns: Column<History.Movie>[] = useMemo<Column<History.Movie>[]>(
|
||||
() => [
|
||||
|
@ -128,7 +131,8 @@ const MoviesHistoryView: FunctionComponent<Props> = () => {
|
|||
<HistoryGenericView
|
||||
type="movies"
|
||||
state={movies}
|
||||
columns={columns as Column<History.Base>[]}
|
||||
loader={loader}
|
||||
columns={columns}
|
||||
></HistoryGenericView>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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<Props> = () => {
|
||||
const series = useSeriesHistory();
|
||||
const loader = useReduxAction(episodesUpdateHistoryByRange);
|
||||
|
||||
const columns: Column<History.Episode>[] = useMemo<Column<History.Episode>[]>(
|
||||
() => [
|
||||
|
@ -137,7 +140,8 @@ const SeriesHistoryView: FunctionComponent<Props> = () => {
|
|||
<HistoryGenericView
|
||||
type="series"
|
||||
state={series}
|
||||
columns={columns as Column<History.Base>[]}
|
||||
loader={loader}
|
||||
columns={columns}
|
||||
></HistoryGenericView>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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<T extends History.Base> {
|
||||
type: "movies" | "series";
|
||||
state: Readonly<Async.Item<History.Base[]>>;
|
||||
columns: Column<History.Base>[];
|
||||
state: Readonly<Async.Entity<T>>;
|
||||
loader: (param: Parameter.Range) => void;
|
||||
columns: Column<T>[];
|
||||
}
|
||||
|
||||
const HistoryGenericView: FunctionComponent<Props> = ({
|
||||
function HistoryGenericView<T extends History.Base = History.Base>({
|
||||
state,
|
||||
loader,
|
||||
columns,
|
||||
type,
|
||||
}) => {
|
||||
}: Props<T>) {
|
||||
const typeName = capitalize(type);
|
||||
return (
|
||||
<Container fluid>
|
||||
|
@ -23,18 +25,16 @@ const HistoryGenericView: FunctionComponent<Props> = ({
|
|||
<title>{typeName} History - Bazarr</title>
|
||||
</Helmet>
|
||||
<Row>
|
||||
<AsyncOverlay ctx={state}>
|
||||
{({ content }) => (
|
||||
<PageTable
|
||||
emptyText={`Nothing Found in ${typeName} History`}
|
||||
columns={columns}
|
||||
data={content ?? []}
|
||||
></PageTable>
|
||||
)}
|
||||
</AsyncOverlay>
|
||||
<AsyncPageTable
|
||||
emptyText={`Nothing Found in ${typeName} History`}
|
||||
entity={state}
|
||||
loader={loader}
|
||||
columns={columns}
|
||||
data={[]}
|
||||
></AsyncPageTable>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default HistoryGenericView;
|
||||
|
|
|
@ -53,16 +53,20 @@ class EpisodeApi extends BaseApi {
|
|||
});
|
||||
}
|
||||
|
||||
async history(episodeid?: number): Promise<History.Episode[]> {
|
||||
return new Promise<History.Episode[]>((resolve, reject) => {
|
||||
this.get<DataWrapper<History.Episode[]>>("/history", { episodeid })
|
||||
.then((result) => {
|
||||
resolve(result.data.data);
|
||||
})
|
||||
.catch((reason) => {
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
async history(params: Parameter.Range) {
|
||||
const response = await this.get<AsyncDataWrapper<History.Episode>>(
|
||||
"/history",
|
||||
params
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async historyBy(episodeid: number) {
|
||||
const response = await this.get<AsyncDataWrapper<History.Episode>>(
|
||||
"/history",
|
||||
{ episodeid }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async downloadSubtitles(
|
||||
|
|
|
@ -93,18 +93,20 @@ class MovieApi extends BaseApi {
|
|||
});
|
||||
}
|
||||
|
||||
async history(id?: number): Promise<History.Movie[]> {
|
||||
return new Promise<History.Movie[]>((resolve, reject) => {
|
||||
this.get<DataWrapper<History.Movie[]>>("/history", {
|
||||
radarrid: id,
|
||||
})
|
||||
.then((result) => {
|
||||
resolve(result.data.data);
|
||||
})
|
||||
.catch((reason) => {
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
async history(params: Parameter.Range) {
|
||||
const response = await this.get<AsyncDataWrapper<History.Movie>>(
|
||||
"/history",
|
||||
params
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async historyBy(radarrid: number) {
|
||||
const response = await this.get<AsyncDataWrapper<History.Movie>>(
|
||||
"/history",
|
||||
{ radarrid }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async action(action: FormType.MoviesAction) {
|
||||
|
|
|
@ -14,8 +14,8 @@ export const MovieHistoryModal: FunctionComponent<BaseModalProps> = (props) => {
|
|||
const movie = useModalPayload<Item.Movie>(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<BaseModalProps> = (props) => {
|
|||
<PageTable
|
||||
emptyText="No History Found"
|
||||
columns={columns}
|
||||
data={content}
|
||||
data={content.data}
|
||||
></PageTable>
|
||||
)}
|
||||
</AsyncOverlay>
|
||||
|
@ -114,8 +114,8 @@ export const EpisodeHistoryModal: FunctionComponent<
|
|||
const episode = useModalPayload<Item.Episode>(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<
|
|||
<PageTable
|
||||
emptyText="No History Found"
|
||||
columns={columns}
|
||||
data={content}
|
||||
data={content.data}
|
||||
></PageTable>
|
||||
)}
|
||||
</AsyncOverlay>
|
||||
|
|
Loading…
Add table
Reference in a new issue