diff --git a/frontend/src/@modules/task/hooks.ts b/frontend/src/@modules/task/hooks.ts index 14fea2288..e2caf269c 100644 --- a/frontend/src/@modules/task/hooks.ts +++ b/frontend/src/@modules/task/hooks.ts @@ -4,10 +4,14 @@ export function useIsAnyTaskRunning() { return BGT.isRunning(); } +export function useIsAnyTaskRunningWithId(id: number) { + return BGT.hasId(id); +} + export function useIsGroupTaskRunning(groupName: string) { return BGT.has(groupName); } -export function useIsIdRunning(groupName: string, id: number) { +export function useIsGroupTaskRunningWithId(groupName: string, id: number) { return BGT.find(groupName, id); } diff --git a/frontend/src/@modules/task/index.ts b/frontend/src/@modules/task/index.ts index 3df9c627d..c58c66614 100644 --- a/frontend/src/@modules/task/index.ts +++ b/frontend/src/@modules/task/index.ts @@ -54,6 +54,16 @@ class BackgroundTask { return groupName in this.groups; } + hasId(id: number) { + for (const key in this.groups) { + const tasks = this.groups[key]; + if (tasks.find((v) => v.id === id) !== undefined) { + return true; + } + } + return false; + } + isRunning() { return keys(this.groups).length > 0; } diff --git a/frontend/src/Movies/Detail/index.tsx b/frontend/src/Movies/Detail/index.tsx index 5adf6265f..2ae3dc37f 100644 --- a/frontend/src/Movies/Detail/index.tsx +++ b/frontend/src/Movies/Detail/index.tsx @@ -11,6 +11,7 @@ import React, { FunctionComponent, useState } from "react"; import { Container, Row } from "react-bootstrap"; import { Helmet } from "react-helmet"; import { Redirect, RouteComponentProps, withRouter } from "react-router-dom"; +import { useIsGroupTaskRunningWithId } from "../../@modules/task/hooks"; import { useMovieBy, useProfileBy } from "../../@redux/hooks"; import { MoviesApi, ProvidersApi } from "../../apis"; import { @@ -23,6 +24,7 @@ import { useShowModal, } from "../../components"; import { ManualSearchModal } from "../../components/modals/ManualSearchModal"; +import { TaskGroupName } from "../../components/modals/MovieUploadModal"; import ItemOverview from "../../generic/ItemOverview"; import { RouterEmptyPath } from "../../special-pages/404"; import { useOnLoadedOnce } from "../../utilites"; @@ -57,6 +59,8 @@ const MovieDetailView: FunctionComponent = ({ match }) => { const [valid, setValid] = useState(true); + const hasTask = useIsGroupTaskRunningWithId(TaskGroupName, id); + useOnLoadedOnce(() => { if (movie.content === null) { setValid(false); @@ -82,6 +86,7 @@ const MovieDetailView: FunctionComponent = ({ match }) => { MoviesApi.action({ action: "scan-disk", radarrid: item.radarrId }) } @@ -90,7 +95,7 @@ const MovieDetailView: FunctionComponent = ({ match }) => { MoviesApi.action({ action: "search-missing", @@ -102,7 +107,7 @@ const MovieDetailView: FunctionComponent = ({ match }) => { showModal("manual-search", item)} > Manual @@ -115,6 +120,7 @@ const MovieDetailView: FunctionComponent = ({ match }) => { showModal("tools", [item])} > Tools @@ -123,7 +129,7 @@ const MovieDetailView: FunctionComponent = ({ match }) => { showModal("upload", item)} > @@ -131,6 +137,7 @@ const MovieDetailView: FunctionComponent = ({ match }) => { showModal("edit", item)} > Edit Movie diff --git a/frontend/src/Movies/index.tsx b/frontend/src/Movies/index.tsx index b6415e6e8..9b274b376 100644 --- a/frontend/src/Movies/index.tsx +++ b/frontend/src/Movies/index.tsx @@ -97,12 +97,14 @@ const MovieView: FunctionComponent = () => { { accessor: "radarrId", selectHide: true, - Cell: ({ row, externalUpdate }) => ( - externalUpdate && externalUpdate(row, "edit")} - > - ), + Cell: ({ row, value, externalUpdate }) => { + return ( + externalUpdate && externalUpdate(row, "edit")} + > + ); + }, }, ], [] diff --git a/frontend/src/components/modals/ItemEditorModal.tsx b/frontend/src/components/modals/ItemEditorModal.tsx index c32c5e247..eea6d7e1e 100644 --- a/frontend/src/components/modals/ItemEditorModal.tsx +++ b/frontend/src/components/modals/ItemEditorModal.tsx @@ -1,6 +1,7 @@ import React, { FunctionComponent, useMemo, useState } from "react"; import { Container, Form } from "react-bootstrap"; import { AsyncButton, Selector } from "../"; +import { useIsAnyTaskRunningWithId } from "../../@modules/task/hooks"; import { useLanguageProfiles } from "../../@redux/hooks"; import { GetItemId } from "../../utilites"; import BaseModal, { BaseModalProps } from "./BaseModal"; @@ -20,6 +21,9 @@ const Editor: FunctionComponent = (props) => { modal.modalKey ); + // TODO: Separate movies and series + const hasTask = useIsAnyTaskRunningWithId(payload ? GetItemId(payload) : -1); + const profileOptions = useMemo[]>( () => profiles?.map((v) => { @@ -31,31 +35,29 @@ const Editor: FunctionComponent = (props) => { const [updating, setUpdating] = useState(false); - const footer = useMemo( - () => ( - { - if (payload) { - const itemId = GetItemId(payload); - return submit({ - id: [itemId], - profileid: [id], - }); - } else { - return null; - } - }} - onSuccess={() => { - closeModal(); - onSuccess && payload && onSuccess(payload); - }} - > - Save - - ), - [closeModal, id, payload, onSuccess, submit] + const footer = ( + { + if (payload) { + const itemId = GetItemId(payload); + return submit({ + id: [itemId], + profileid: [id], + }); + } else { + return null; + } + }} + onSuccess={() => { + closeModal(); + onSuccess && payload && onSuccess(payload); + }} + > + Save + ); return ( @@ -81,6 +83,7 @@ const Editor: FunctionComponent = (props) => { Languages Profiles setId(v === undefined ? null : v)} diff --git a/frontend/src/components/modals/MovieUploadModal.tsx b/frontend/src/components/modals/MovieUploadModal.tsx index 1369c13c4..cebb7b925 100644 --- a/frontend/src/components/modals/MovieUploadModal.tsx +++ b/frontend/src/components/modals/MovieUploadModal.tsx @@ -1,6 +1,9 @@ import React, { FunctionComponent, useEffect, useMemo, useState } from "react"; -import { Container, Form } from "react-bootstrap"; -import { AsyncButton, FileForm, LanguageSelector } from ".."; +import { Button, Container, Form } from "react-bootstrap"; +import { FileForm, LanguageSelector } from ".."; +import BackgroundTask from "../../@modules/task"; +import { useIsGroupTaskRunning } from "../../@modules/task/hooks"; +import { createTask } from "../../@modules/task/utilites"; import { useEnabledLanguages, useLanguageBy, @@ -9,11 +12,10 @@ import { import { MoviesApi } from "../../apis"; import BaseModal, { BaseModalProps } from "./BaseModal"; import { useModalInformation } from "./hooks"; -interface MovieProps {} -const MovieUploadModal: FunctionComponent = ( - props -) => { +export const TaskGroupName = "Uploading Movie Subtitles..."; + +const MovieUploadModal: FunctionComponent = (props) => { const modal = props; const availableLanguages = useEnabledLanguages(); @@ -22,8 +24,6 @@ const MovieUploadModal: FunctionComponent = ( modal.modalKey ); - const [uploading, setUpload] = useState(false); - const [language, setLanguage] = useState>(null); const profile = useProfileBy(payload?.profileId); @@ -35,40 +35,41 @@ const MovieUploadModal: FunctionComponent = ( const [file, setFile] = useState>(null); const [forced, setForced] = useState(false); + const hasTask = useIsGroupTaskRunning(TaskGroupName); + const canUpload = useMemo(() => { - return file !== null && language?.code2; - }, [language, file]); + return file !== null && language?.code2 && !hasTask; + }, [language, file, hasTask]); const footer = ( - { + onClick={() => { if (file && payload && language) { - return MoviesApi.uploadSubtitles(payload.radarrId, { - file: file, - forced, - hi: false, - language: language.code2, - }); - } else { - return null; + const id = payload.radarrId; + const task = createTask( + file.name, + id, + MoviesApi.uploadSubtitles.bind(MoviesApi), + id, + { + file: file, + forced, + hi: false, + language: language.code2, + } + ); + BackgroundTask.dispatch(TaskGroupName, [task]); + closeModal(props.modalKey); } }} - onSuccess={() => closeModal()} > Upload - + ); return ( - +