diff --git a/src/main/Discard.tsx b/src/main/Discard.tsx index 168f59796..93650348d 100644 --- a/src/main/Discard.tsx +++ b/src/main/Discard.tsx @@ -5,8 +5,7 @@ import { basicButtonStyle, backOrContinueStyle, navigationButtonStyle } from ".. import { LuChevronLeft, LuXCircle } from "react-icons/lu"; -import { useAppDispatch, useAppSelector } from "../redux/store"; -import { selectFinishState } from "../redux/finishSlice"; +import { useAppDispatch } from "../redux/store"; import { setEnd } from "../redux/endSlice"; import { PageButton } from "./Finish"; @@ -22,10 +21,8 @@ const Discard: React.FC = () => { const { t } = useTranslation(); - const finishState = useAppSelector(selectFinishState); - const cancelStyle = css({ - display: finishState !== "Discard changes" ? "none" : "flex", + display: "flex", flexDirection: "column" as const, alignItems: "center", gap: "30px", diff --git a/src/main/Finish.tsx b/src/main/Finish.tsx index 2905ad76a..0f93dd44d 100644 --- a/src/main/Finish.tsx +++ b/src/main/Finish.tsx @@ -14,7 +14,7 @@ import { basicButtonStyle, navigationButtonStyle } from "../cssStyles"; import { IconType } from "react-icons"; import { useAppDispatch, useAppSelector } from "../redux/store"; -import { selectPageNumber, setPageNumber } from "../redux/finishSlice"; +import { selectFinishState, selectPageNumber, setPageNumber } from "../redux/finishSlice"; import { useTheme } from "../themes"; import { settings } from "../config"; import { useTranslation } from "react-i18next"; @@ -25,33 +25,36 @@ import { useTranslation } from "react-i18next"; const Finish: React.FC = () => { const pageNumber = useAppSelector(selectPageNumber); + const finishState = useAppSelector(selectFinishState); - const pageZeroStyle = css({ - display: pageNumber !== 0 ? "none" : "block", - }); - - const pageOneStyle = css({ - display: pageNumber !== 1 ? "none" : "block", - }); - - const pageTwoStyle = css({ - display: pageNumber !== 2 ? "none" : "block", - }); - - return ( -
-
+ const render = () => { + if (pageNumber === 0) { + return ( -
-
- - - -
-
+ ); + } else if (pageNumber === 1) { + if (finishState === "Save changes") { + return ( + + ); + } else if (finishState === "Start processing") { + return ( + + ); + } else if (finishState === "Discard changes") { + return ( + + ); + } + } else if (pageNumber === 2) { + return ( -
-
+ ); + } + }; + + return ( + <>{render()} ); }; diff --git a/src/main/Save.tsx b/src/main/Save.tsx index b1241c2e2..7e9323674 100644 --- a/src/main/Save.tsx +++ b/src/main/Save.tsx @@ -9,7 +9,6 @@ import { import { LuLoader, LuCheckCircle, LuAlertCircle, LuChevronLeft, LuSave, LuCheck } from "react-icons/lu"; import { useAppDispatch, useAppSelector } from "../redux/store"; -import { selectFinishState } from "../redux/finishSlice"; import { selectHasChanges, selectSegments, @@ -33,6 +32,8 @@ import { import { serializeSubtitle } from "../util/utilityFunctions"; import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; +import { IconType } from "react-icons"; +import { setEnd } from "../redux/endSlice"; /** * Shown if the user wishes to save. @@ -42,8 +43,6 @@ const Save: React.FC = () => { const { t } = useTranslation(); - const finishState = useAppSelector(selectFinishState); - const postWorkflowStatus = useAppSelector(selectStatus); const postError = useAppSelector(selectError); const theme = useTheme(); @@ -52,9 +51,9 @@ const Save: React.FC = () => { const subtitleHasChanges = useAppSelector(selectSubtitleHasChanges); const saveStyle = css({ - height: "100%", - display: finishState !== "Save changes" ? "none" : "flex", - flexDirection: "column" as const, + display: "flex", + flexDirection: "column", + justifyContent: "center", alignItems: "center", gap: "30px", }); @@ -101,8 +100,15 @@ const Save: React.FC = () => { /** * Button that sends a post request to save current changes */ -export const SaveButton: React.FC = () => { - +export const SaveButton: React.FC<{ + basicIcon?: IconType + text?: string + isTransitionToEnd?: boolean +}> = ({ + basicIcon = LuSave, + text, + isTransitionToEnd = false, +}) => { const { t } = useTranslation(); // Initialize redux variables @@ -116,7 +122,7 @@ export const SaveButton: React.FC = () => { const theme = useTheme(); // Update based on current fetching status - let Icon = LuSave; + let Icon = basicIcon; let spin = false; let tooltip = null; if (workflowStatus === "failed") { @@ -164,6 +170,9 @@ export const SaveButton: React.FC = () => { // Let users leave the page without warning after a successful save useEffect(() => { if (workflowStatus === "success") { + if (isTransitionToEnd) { + dispatch(setEnd({ hasEnded: true, value: "success" })); + } dispatch(videoSetHasChanges(false)); dispatch(metadataSetHasChanges(false)); dispatch(subtitleSetHasChanges(false)); @@ -181,7 +190,7 @@ export const SaveButton: React.FC = () => { } }}> - {t("save.confirm-button")} + {text ?? t("save.confirm-button")}
{ariaSaveUpdate()}
diff --git a/src/main/WorkflowConfiguration.tsx b/src/main/WorkflowConfiguration.tsx index 1c8930cf4..f736c201d 100644 --- a/src/main/WorkflowConfiguration.tsx +++ b/src/main/WorkflowConfiguration.tsx @@ -1,38 +1,21 @@ -import React, { useEffect } from "react"; +import React from "react"; import { css } from "@emotion/react"; import { - basicButtonStyle, backOrContinueStyle, errorBoxStyle, - spinningStyle, } from "../cssStyles"; -import { LuLoader, LuCheck, LuAlertCircle, LuChevronLeft, LuDatabase, LuMoreHorizontal } from "react-icons/lu"; +import { LuChevronLeft, LuDatabase, LuMoreHorizontal } from "react-icons/lu"; -import { useAppDispatch, useAppSelector } from "../redux/store"; -import { - selectSegments, - selectTracks, - setHasChanges as videoSetHasChanges, - selectSelectedWorkflowId, -} from "../redux/videoSlice"; -import { postVideoInformationWithWorkflow, selectStatus, selectError } from "../redux/workflowPostAndProcessSlice"; +import { useAppSelector } from "../redux/store"; import { PageButton } from "./Finish"; -import { setEnd } from "../redux/endSlice"; import { useTranslation } from "react-i18next"; -import { - setHasChanges as metadataSetHasChanges, - selectCatalogs, -} from "../redux/metadataSlice"; -import { - selectSubtitles, - setHasChanges as subtitleSetHasChanges, -} from "../redux/subtitleSlice"; -import { serializeSubtitle } from "../util/utilityFunctions"; import { useTheme } from "../themes"; +import { selectError, selectStatus } from "../redux/workflowPostSlice"; +import { SaveButton } from "./Save"; /** * Will eventually display settings based on the selected workflow index @@ -61,7 +44,11 @@ const WorkflowConfiguration: React.FC = () => {
{t("workflowConfig.satisfied-text")}
- +
{t("various.error-text")}
@@ -73,90 +60,4 @@ const WorkflowConfiguration: React.FC = () => { ); }; -/** - * Button that sends a post request to save current changes - * and starts the selected workflow - */ -export const SaveAndProcessButton: React.FC<{ text: string; }> = ({ text }) => { - - // Initialize redux variables - const dispatch = useAppDispatch(); - - const selectedWorkflowId = useAppSelector(selectSelectedWorkflowId); - const segments = useAppSelector(selectSegments); - const tracks = useAppSelector(selectTracks); - const subtitles = useAppSelector(selectSubtitles); - const metadata = useAppSelector(selectCatalogs); - const workflowStatus = useAppSelector(selectStatus); - const theme = useTheme(); - - // Let users leave the page without warning after a successful save - useEffect(() => { - if (workflowStatus === "success") { - dispatch(setEnd({ hasEnded: true, value: "success" })); - dispatch(videoSetHasChanges(false)); - dispatch(metadataSetHasChanges(false)); - dispatch(subtitleSetHasChanges(false)); - } - }, [dispatch, workflowStatus]); - - const prepareSubtitles = () => { - const subtitlesForPosting = []; - - for (const identifier in subtitles) { - subtitlesForPosting.push({ - id: identifier, - subtitle: serializeSubtitle(subtitles[identifier].cues), - tags: subtitles[identifier].tags, - }); - } - return subtitlesForPosting; - }; - - const saveAndProcess = () => { - dispatch(postVideoInformationWithWorkflow({ - segments: segments, - tracks: tracks, - workflow: [{ id: selectedWorkflowId }], - subtitles: prepareSubtitles(), - metadata: metadata, - })); - }; - - // Update based on current fetching status - let Icon = LuDatabase; - let spin = false; - if (workflowStatus === "failed") { - Icon = LuAlertCircle; - spin = false; - } else if (workflowStatus === "success") { - Icon = LuCheck; - spin = false; - } else if (workflowStatus === "loading") { - Icon = LuLoader; - spin = true; - - } - - const saveButtonStyle = css({ - padding: "16px", - boxShadow: `${theme.boxShadow}`, - background: `${theme.element_bg}`, - }); - - return ( -
) => { - if (event.key === " " || event.key === "Enter") { - saveAndProcess(); - } - }}> - - {text} -
- ); -}; - export default WorkflowConfiguration; diff --git a/src/main/WorkflowSelection.tsx b/src/main/WorkflowSelection.tsx index 19b773358..48fb01a92 100644 --- a/src/main/WorkflowSelection.tsx +++ b/src/main/WorkflowSelection.tsx @@ -5,12 +5,9 @@ import { backOrContinueStyle, errorBoxStyle } from "../cssStyles"; import { useAppDispatch, useAppSelector } from "../redux/store"; import { selectWorkflows, setSelectedWorkflowIndex } from "../redux/videoSlice"; -import { selectFinishState, selectPageNumber } from "../redux/finishSlice"; import { PageButton } from "./Finish"; -import { LuChevronLeft } from "react-icons/lu"; -import { SaveAndProcessButton } from "./WorkflowConfiguration"; -import { selectStatus, selectError } from "../redux/workflowPostAndProcessSlice"; +import { LuChevronLeft, LuDatabase } from "react-icons/lu"; import { selectStatus as saveSelectStatus, selectError as saveSelectError } from "../redux/workflowPostSlice"; import { httpRequestState, Workflow } from "../types"; import { SaveButton } from "./Save"; @@ -37,18 +34,14 @@ const WorkflowSelection: React.FC = () => { return (b.displayOrder - a.displayOrder); }); - const finishState = useAppSelector(selectFinishState); - const pageNumber = useAppSelector(selectPageNumber); const theme = useTheme(); - const postAndProcessWorkflowStatus = useAppSelector(selectStatus); - const postAndProcessError = useAppSelector(selectError); const saveStatus = useAppSelector(saveSelectStatus); const saveError = useAppSelector(saveSelectError); const workflowSelectionStyle = css({ padding: "20px", - display: (finishState === "Start processing" && pageNumber === 1) ? "flex" : "none", + display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", @@ -107,7 +100,7 @@ const WorkflowSelection: React.FC = () => {
{t("various.error-text")}
{errorMessage ? - t("various.error-details-text", { errorMessage: postAndProcessError }) : + t("various.error-details-text", { errorMessage: saveError }) : t("various.error-text")}
@@ -140,9 +133,13 @@ const WorkflowSelection: React.FC = () => { This will take some time. , false, - , - postAndProcessWorkflowStatus, - postAndProcessError + , + saveStatus, + saveError ) ); } else { @@ -153,9 +150,13 @@ const WorkflowSelection: React.FC = () => { {t("workflowSelection.manyWorkflows-text")} , true, - , - postAndProcessWorkflowStatus, - postAndProcessError + , + saveStatus, + saveError ) ); } diff --git a/src/redux/store.ts b/src/redux/store.ts index bfedf0db1..b7dd64bbb 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -3,7 +3,6 @@ import mainMenuStateReducer from "./mainMenuSlice"; import finishStateReducer from "./finishSlice"; import videoReducer from "./videoSlice"; import workflowPostReducer from "./workflowPostSlice"; -import workflowPostAndProcessReducer from "./workflowPostAndProcessSlice"; import endReducer from "./endSlice"; import metadataReducer from "./metadataSlice"; import subtitleReducer from "./subtitleSlice"; @@ -16,7 +15,6 @@ export const store = configureStore({ finishState: finishStateReducer, videoState: videoReducer, workflowPostState: workflowPostReducer, - workflowPostAndProcessState: workflowPostAndProcessReducer, endState: endReducer, metadataState: metadataReducer, subtitleState: subtitleReducer, diff --git a/src/redux/workflowPostAndProcessSlice.ts b/src/redux/workflowPostAndProcessSlice.ts deleted file mode 100644 index 6bb6e3491..000000000 --- a/src/redux/workflowPostAndProcessSlice.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; -import { client } from "../util/client"; -import { PostAndProcessEditArgument, httpRequestState } from "../types"; - -import { convertSegments } from "./workflowPostSlice"; -import { settings } from "../config"; - -const initialState: httpRequestState = { - status: "idle", - error: undefined, - errorReason: "unknown", -}; - -export const postVideoInformationWithWorkflow = - createAsyncThunk("video/postVideoInformationWithWorkflow", async (argument: PostAndProcessEditArgument) => { - if (!settings.id) { - throw new Error("Missing media package identifier"); - } - - const response = await client.post(`${settings.opencast.url}/editor/${settings.id}/edit.json`, - { - segments: convertSegments(argument.segments), - tracks: argument.tracks, - subtitles: argument.subtitles, - metadataJSON: JSON.stringify(argument.metadata), - workflows: argument.workflow, - } - ); - return response; - }); - -/** - * Slice for managing a post request for saving current changes and starting a workflow - * TODO: Create a wrapper for this and workflowPostAndProcessSlice - */ -const workflowPostAndProcessSlice = createSlice({ - name: "workflowPostAndProcessState", - initialState, - reducers: { - }, - extraReducers: builder => { - builder.addCase( - postVideoInformationWithWorkflow.pending, (state, _action) => { - state.status = "loading"; - }); - builder.addCase( - postVideoInformationWithWorkflow.fulfilled, (state, _action) => { - state.status = "success"; - }); - builder.addCase( - postVideoInformationWithWorkflow.rejected, (state, action) => { - state.status = "failed"; - state.error = action.error.message; - }); - }, - selectors: { - selectStatus: state => state.status, - selectError: state => state.error, - }, -}); - -export const { selectStatus, selectError } = workflowPostAndProcessSlice.selectors; - - -export default workflowPostAndProcessSlice.reducer; diff --git a/src/redux/workflowPostSlice.ts b/src/redux/workflowPostSlice.ts index 11564c797..a86291646 100644 --- a/src/redux/workflowPostSlice.ts +++ b/src/redux/workflowPostSlice.ts @@ -20,6 +20,7 @@ export const postVideoInformation = segments: convertSegments(argument.segments), tracks: argument.tracks, subtitles: argument.subtitles, + workflows: argument.workflow, metadataJSON: JSON.stringify(argument.metadata), } ); diff --git a/src/types.ts b/src/types.ts index d54f904db..1ee6ff3cd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -70,13 +70,10 @@ export interface PostEditArgument { segments: Segment[] tracks: Track[] subtitles: SubtitlesFromOpencast[] + workflow?: [{id: string}] metadata: Catalog[] } -export interface PostAndProcessEditArgument extends PostEditArgument{ - workflow: [{id: string}] -} - // Use respective i18n keys as values export enum MainMenuStateNames { cutting = "mainMenu.cutting-button",