mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-18 21:35:27 -04:00
Use react-query for renaming
This commit is contained in:
@@ -3,7 +3,6 @@ import CaptchaAppState from './CaptchaAppState';
|
||||
import ImportSeriesAppState from './ImportSeriesAppState';
|
||||
import InteractiveImportAppState from './InteractiveImportAppState';
|
||||
import OAuthAppState from './OAuthAppState';
|
||||
import OrganizePreviewAppState from './OrganizePreviewAppState';
|
||||
import ProviderOptionsAppState from './ProviderOptionsAppState';
|
||||
import SettingsAppState from './SettingsAppState';
|
||||
|
||||
@@ -13,7 +12,6 @@ interface AppState {
|
||||
importSeries: ImportSeriesAppState;
|
||||
interactiveImport: InteractiveImportAppState;
|
||||
oAuth: OAuthAppState;
|
||||
organizePreview: OrganizePreviewAppState;
|
||||
providerOptions: ProviderOptionsAppState;
|
||||
settings: SettingsAppState;
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
import AppSectionState from 'App/State/AppSectionState';
|
||||
|
||||
export interface OrganizePreviewModel extends ModelBase {
|
||||
seriesId: number;
|
||||
seasonNumber: number;
|
||||
episodeNumbers: number[];
|
||||
episodeFileId: number;
|
||||
existingPath: string;
|
||||
newPath: string;
|
||||
}
|
||||
|
||||
type OrganizePreviewAppState = AppSectionState<OrganizePreviewModel>;
|
||||
|
||||
export default OrganizePreviewAppState;
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import { clearOrganizePreview } from 'Store/Actions/organizePreviewActions';
|
||||
import OrganizePreviewModalContent, {
|
||||
OrganizePreviewModalContentProps,
|
||||
} from './OrganizePreviewModalContent';
|
||||
@@ -16,19 +14,12 @@ function OrganizePreviewModal({
|
||||
onModalClose,
|
||||
...otherProps
|
||||
}: OrganizePreviewModalProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleOnModalClose = useCallback(() => {
|
||||
dispatch(clearOrganizePreview());
|
||||
onModalClose();
|
||||
}, [dispatch, onModalClose]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onModalClose={handleOnModalClose}>
|
||||
<Modal isOpen={isOpen} onModalClose={onModalClose}>
|
||||
{isOpen ? (
|
||||
<OrganizePreviewModalContent
|
||||
{...otherProps}
|
||||
onModalClose={handleOnModalClose}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
) : null}
|
||||
</Modal>
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { SelectProvider, useSelect } from 'App/Select/SelectContext';
|
||||
import AppState from 'App/State/AppState';
|
||||
import { OrganizePreviewModel } from 'App/State/OrganizePreviewAppState';
|
||||
import CommandNames from 'Commands/CommandNames';
|
||||
import { useExecuteCommand } from 'Commands/useCommands';
|
||||
import Alert from 'Components/Alert';
|
||||
@@ -17,11 +16,11 @@ import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import formatSeason from 'Season/formatSeason';
|
||||
import { useSingleSeries } from 'Series/useSeries';
|
||||
import { fetchOrganizePreview } from 'Store/Actions/organizePreviewActions';
|
||||
import { fetchNamingSettings } from 'Store/Actions/settingsActions';
|
||||
import { CheckInputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import OrganizePreviewRow from './OrganizePreviewRow';
|
||||
import useOrganizePreview, { OrganizePreviewModel } from './useOrganizePreview';
|
||||
import styles from './OrganizePreviewModalContent.css';
|
||||
|
||||
function getValue(allSelected: boolean, allUnselected: boolean) {
|
||||
@@ -50,9 +49,9 @@ function OrganizePreviewModalContentInner({
|
||||
const {
|
||||
items,
|
||||
isFetching: isPreviewFetching,
|
||||
isPopulated: isPreviewPopulated,
|
||||
isFetched: isPreviewFetched,
|
||||
error: previewError,
|
||||
} = useSelector((state: AppState) => state.organizePreview);
|
||||
} = useOrganizePreview(seriesId, seasonNumber);
|
||||
|
||||
const {
|
||||
isFetching: isNamingFetching,
|
||||
@@ -67,7 +66,7 @@ function OrganizePreviewModalContentInner({
|
||||
useSelect<OrganizePreviewModel>();
|
||||
|
||||
const isFetching = isPreviewFetching || isNamingFetching;
|
||||
const isPopulated = isPreviewPopulated && isNamingPopulated;
|
||||
const isPopulated = isPreviewFetched && isNamingPopulated;
|
||||
const error = previewError || namingError;
|
||||
const { renameEpisodes } = naming;
|
||||
const episodeFormat = naming[`${series.seriesType}EpisodeFormat`];
|
||||
@@ -98,9 +97,8 @@ function OrganizePreviewModalContentInner({
|
||||
}, [seriesId, getSelectedIds, executeCommand, onModalClose]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchOrganizePreview({ seriesId, seasonNumber }));
|
||||
dispatch(fetchNamingSettings());
|
||||
}, [seriesId, seasonNumber, dispatch]);
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
@@ -191,7 +189,7 @@ function OrganizePreviewModalContent({
|
||||
seasonNumber,
|
||||
onModalClose,
|
||||
}: OrganizePreviewModalContentProps) {
|
||||
const { items } = useSelector((state: AppState) => state.organizePreview);
|
||||
const { items } = useOrganizePreview(seriesId, seasonNumber);
|
||||
|
||||
return (
|
||||
<SelectProvider<OrganizePreviewModel> items={items}>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useSelect } from 'App/Select/SelectContext';
|
||||
import { OrganizePreviewModel } from 'App/State/OrganizePreviewAppState';
|
||||
import CheckInput from 'Components/Form/CheckInput';
|
||||
import Icon from 'Components/Icon';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import { CheckInputChanged } from 'typings/inputs';
|
||||
import { OrganizePreviewModel } from './useOrganizePreview';
|
||||
import styles from './OrganizePreviewRow.css';
|
||||
|
||||
interface OrganizePreviewRowProps {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
import useApiQuery from 'Helpers/Hooks/useApiQuery';
|
||||
|
||||
export interface OrganizePreviewModel extends ModelBase {
|
||||
seriesId: number;
|
||||
seasonNumber: number;
|
||||
episodeNumbers: number[];
|
||||
episodeFileId: number;
|
||||
existingPath: string;
|
||||
newPath: string;
|
||||
}
|
||||
|
||||
const DEFAULT_ORGANIZE_PREVIEW: OrganizePreviewModel[] = [];
|
||||
|
||||
const useOrganizePreview = (seriesId: number, seasonNumber?: number) => {
|
||||
const queryParams: { seriesId: number; seasonNumber?: number } = { seriesId };
|
||||
|
||||
if (seasonNumber != null) {
|
||||
queryParams.seasonNumber = seasonNumber;
|
||||
}
|
||||
|
||||
const { data, ...result } = useApiQuery<OrganizePreviewModel[]>({
|
||||
path: '/rename',
|
||||
queryParams,
|
||||
});
|
||||
|
||||
return {
|
||||
items: data ?? DEFAULT_ORGANIZE_PREVIEW,
|
||||
...result,
|
||||
};
|
||||
};
|
||||
|
||||
export default useOrganizePreview;
|
||||
@@ -2,7 +2,6 @@ import * as captcha from './captchaActions';
|
||||
import * as importSeries from './importSeriesActions';
|
||||
import * as interactiveImportActions from './interactiveImportActions';
|
||||
import * as oAuth from './oAuthActions';
|
||||
import * as organizePreview from './organizePreviewActions';
|
||||
import * as providerOptions from './providerOptionActions';
|
||||
import * as settings from './settingsActions';
|
||||
|
||||
@@ -11,7 +10,6 @@ export default [
|
||||
importSeries,
|
||||
interactiveImportActions,
|
||||
oAuth,
|
||||
organizePreview,
|
||||
providerOptions,
|
||||
settings
|
||||
];
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { createAction } from 'redux-actions';
|
||||
import { createThunk, handleThunks } from 'Store/thunks';
|
||||
import createFetchHandler from './Creators/createFetchHandler';
|
||||
import createHandleActions from './Creators/createHandleActions';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
||||
export const section = 'organizePreview';
|
||||
|
||||
//
|
||||
// State
|
||||
|
||||
export const defaultState = {
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
error: null,
|
||||
items: []
|
||||
};
|
||||
|
||||
//
|
||||
// Actions Types
|
||||
|
||||
export const FETCH_ORGANIZE_PREVIEW = 'organizePreview/fetchOrganizePreview';
|
||||
export const CLEAR_ORGANIZE_PREVIEW = 'organizePreview/clearOrganizePreview';
|
||||
|
||||
//
|
||||
// Action Creators
|
||||
|
||||
export const fetchOrganizePreview = createThunk(FETCH_ORGANIZE_PREVIEW);
|
||||
export const clearOrganizePreview = createAction(CLEAR_ORGANIZE_PREVIEW);
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
export const actionHandlers = handleThunks({
|
||||
|
||||
[FETCH_ORGANIZE_PREVIEW]: createFetchHandler('organizePreview', '/rename')
|
||||
|
||||
});
|
||||
|
||||
//
|
||||
// Reducers
|
||||
|
||||
export const reducers = createHandleActions({
|
||||
|
||||
[CLEAR_ORGANIZE_PREVIEW]: (state) => {
|
||||
return Object.assign({}, state, defaultState);
|
||||
}
|
||||
|
||||
}, defaultState, section);
|
||||
@@ -1,49 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using Sonarr.Http;
|
||||
using Sonarr.Http.REST;
|
||||
|
||||
namespace Sonarr.Api.V3.Episodes
|
||||
namespace Sonarr.Api.V5.Episodes;
|
||||
|
||||
[V5ApiController("rename")]
|
||||
public class RenameEpisodeController : Controller
|
||||
{
|
||||
[V3ApiController("rename")]
|
||||
public class RenameEpisodeController : Controller
|
||||
private readonly IRenameEpisodeFileService _renameEpisodeFileService;
|
||||
|
||||
public RenameEpisodeController(IRenameEpisodeFileService renameEpisodeFileService)
|
||||
{
|
||||
private readonly IRenameEpisodeFileService _renameEpisodeFileService;
|
||||
_renameEpisodeFileService = renameEpisodeFileService;
|
||||
}
|
||||
|
||||
public RenameEpisodeController(IRenameEpisodeFileService renameEpisodeFileService)
|
||||
[HttpGet]
|
||||
[Produces("application/json")]
|
||||
public List<RenameEpisodeResource> GetEpisodes(int seriesId, int? seasonNumber)
|
||||
{
|
||||
if (seasonNumber.HasValue)
|
||||
{
|
||||
_renameEpisodeFileService = renameEpisodeFileService;
|
||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber.Value).ToResource();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Produces("application/json")]
|
||||
public List<RenameEpisodeResource> GetEpisodes(int seriesId, int? seasonNumber)
|
||||
{
|
||||
if (seasonNumber.HasValue)
|
||||
{
|
||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber.Value).ToResource();
|
||||
}
|
||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource();
|
||||
}
|
||||
|
||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource();
|
||||
[HttpGet("bulk")]
|
||||
[Produces("application/json")]
|
||||
public List<RenameEpisodeResource> GetEpisodes([FromQuery] List<int> seriesIds)
|
||||
{
|
||||
if (seriesIds is { Count: 0 })
|
||||
{
|
||||
throw new BadRequestException("seriesIds must be provided");
|
||||
}
|
||||
|
||||
[HttpGet("bulk")]
|
||||
[Produces("application/json")]
|
||||
public List<RenameEpisodeResource> GetEpisodes([FromQuery] List<int> seriesIds)
|
||||
if (seriesIds.Any(seriesId => seriesId <= 0))
|
||||
{
|
||||
if (seriesIds is { Count: 0 })
|
||||
{
|
||||
throw new BadRequestException("seriesIds must be provided");
|
||||
}
|
||||
|
||||
if (seriesIds.Any(seriesId => seriesId <= 0))
|
||||
{
|
||||
throw new BadRequestException("seriesIds must be positive integers");
|
||||
}
|
||||
|
||||
return _renameEpisodeFileService.GetRenamePreviews(seriesIds).ToResource();
|
||||
throw new BadRequestException("seriesIds must be positive integers");
|
||||
}
|
||||
|
||||
return _renameEpisodeFileService.GetRenamePreviews(seriesIds).ToResource();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Sonarr.Http.REST;
|
||||
using Sonarr.Http.REST;
|
||||
|
||||
namespace Sonarr.Api.V3.Episodes
|
||||
namespace Sonarr.Api.V5.Episodes;
|
||||
|
||||
public class RenameEpisodeResource : RestResource
|
||||
{
|
||||
public class RenameEpisodeResource : RestResource
|
||||
public int SeriesId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public List<int> EpisodeNumbers { get; set; } = [];
|
||||
public int EpisodeFileId { get; set; }
|
||||
public string? ExistingPath { get; set; }
|
||||
public string? NewPath { get; set; }
|
||||
}
|
||||
|
||||
public static class RenameEpisodeResourceMapper
|
||||
{
|
||||
public static RenameEpisodeResource ToResource(this NzbDrone.Core.MediaFiles.RenameEpisodeFilePreview model)
|
||||
{
|
||||
public int SeriesId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public List<int> EpisodeNumbers { get; set; }
|
||||
public int EpisodeFileId { get; set; }
|
||||
public string ExistingPath { get; set; }
|
||||
public string NewPath { get; set; }
|
||||
return new RenameEpisodeResource
|
||||
{
|
||||
Id = model.EpisodeFileId,
|
||||
SeriesId = model.SeriesId,
|
||||
SeasonNumber = model.SeasonNumber,
|
||||
EpisodeNumbers = model.EpisodeNumbers.ToList(),
|
||||
EpisodeFileId = model.EpisodeFileId,
|
||||
ExistingPath = model.ExistingPath,
|
||||
NewPath = model.NewPath
|
||||
};
|
||||
}
|
||||
|
||||
public static class RenameEpisodeResourceMapper
|
||||
public static List<RenameEpisodeResource> ToResource(this IEnumerable<NzbDrone.Core.MediaFiles.RenameEpisodeFilePreview> models)
|
||||
{
|
||||
public static RenameEpisodeResource ToResource(this NzbDrone.Core.MediaFiles.RenameEpisodeFilePreview model)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RenameEpisodeResource
|
||||
{
|
||||
Id = model.EpisodeFileId,
|
||||
SeriesId = model.SeriesId,
|
||||
SeasonNumber = model.SeasonNumber,
|
||||
EpisodeNumbers = model.EpisodeNumbers.ToList(),
|
||||
EpisodeFileId = model.EpisodeFileId,
|
||||
ExistingPath = model.ExistingPath,
|
||||
NewPath = model.NewPath
|
||||
};
|
||||
}
|
||||
|
||||
public static List<RenameEpisodeResource> ToResource(this IEnumerable<NzbDrone.Core.MediaFiles.RenameEpisodeFilePreview> models)
|
||||
{
|
||||
return models.Select(ToResource).ToList();
|
||||
}
|
||||
return models.Select(ToResource).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user