From 26ac0c55a9421181b7c8742d2836beffd44e83b6 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 9 Jul 2024 16:37:55 +0200 Subject: [PATCH] Remove duplicate save code This removes duplicate code regarding saving. Functionally, saving and starting workflows should still work the same. Also slightly improves on how we render the finish view, cleaning up our dom tree a bit. --- src/main/Discard.tsx | 7 +- src/main/Finish.tsx | 53 ++++----- src/main/Save.tsx | 32 ++++-- src/main/WorkflowConfiguration.tsx | 130 ++--------------------- src/main/WorkflowSelection.tsx | 33 +++--- src/redux/store.ts | 2 - src/redux/workflowPostAndProcessSlice.ts | 64 ----------- src/redux/workflowPostSlice.ts | 1 + src/types.ts | 5 +- 9 files changed, 81 insertions(+), 246 deletions(-) delete mode 100644 src/redux/workflowPostAndProcessSlice.ts 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 cc78e929b..3ea590c77 100644 --- a/src/main/Save.tsx +++ b/src/main/Save.tsx @@ -9,10 +9,10 @@ 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, + selectSelectedWorkflowId, selectTracks, setHasChanges as videoSetHasChanges, } from "../redux/videoSlice"; @@ -32,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. @@ -41,8 +43,6 @@ const Save: React.FC = () => { const { t } = useTranslation(); - const finishState = useAppSelector(selectFinishState); - const postWorkflowStatus = useAppSelector(selectStatus); const postError = useAppSelector(selectError); const postMetadataStatus = useAppSelector(selectPostStatus); @@ -53,9 +53,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", }); @@ -108,8 +108,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 @@ -120,11 +127,12 @@ export const SaveButton: React.FC = () => { const subtitles = useAppSelector(selectSubtitles); const workflowStatus = useAppSelector(selectStatus); const metadataStatus = useAppSelector(selectPostStatus); + const selectedWorkflowId = useAppSelector(selectSelectedWorkflowId); const theme = useTheme(); const [metadataSaveStarted, setMetadataSaveStarted] = useState(false); // Update based on current fetching status - let Icon = LuSave; + let Icon = basicIcon; let spin = false; let tooltip = null; if (workflowStatus === "failed" || metadataStatus === "failed") { @@ -176,6 +184,7 @@ export const SaveButton: React.FC = () => { segments: segments, tracks: tracks, subtitles: prepareSubtitles(), + workflow: selectedWorkflowId ? [{ id: selectedWorkflowId }] : undefined, })); } @@ -185,6 +194,9 @@ export const SaveButton: React.FC = () => { // Let users leave the page without warning after a successful save useEffect(() => { if (workflowStatus === "success" && metadataStatus === "success") { + if (isTransitionToEnd) { + dispatch(setEnd({ hasEnded: true, value: "success" })); + } dispatch(videoSetHasChanges(false)); dispatch(metadataSetHasChanges(false)); dispatch(subtitleSetHasChanges(false)); @@ -202,7 +214,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 fabdbd4bd..bd3edc786 100644 --- a/src/main/WorkflowConfiguration.tsx +++ b/src/main/WorkflowConfiguration.tsx @@ -1,40 +1,25 @@ -import React, { useEffect, useState } 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 { - postMetadata, selectPostError, selectPostStatus, - setHasChanges as metadataSetHasChanges, } 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 @@ -65,7 +50,11 @@ const WorkflowConfiguration: React.FC = () => {
{t("workflowConfig.satisfied-text")}
- +
{t("various.error-text")}
@@ -83,103 +72,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 workflowStatus = useAppSelector(selectStatus); - const metadataStatus = useAppSelector(selectPostStatus); - const [metadataSaveStarted, setMetadataSaveStarted] = useState(false); - const theme = useTheme(); - - // Let users leave the page without warning after a successful save - useEffect(() => { - if (workflowStatus === "success" && metadataStatus === "success") { - dispatch(setEnd({ hasEnded: true, value: "success" })); - dispatch(videoSetHasChanges(false)); - dispatch(metadataSetHasChanges(false)); - dispatch(subtitleSetHasChanges(false)); - } - }, [dispatch, metadataStatus, 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; - }; - - // Dispatches first save request - // Subsequent save requests should be wrapped in useEffect hooks, - // so they are only sent after the previous one has finished - const saveAndProcess = () => { - setMetadataSaveStarted(true); - dispatch(postMetadata()); - }; - - // Subsequent save request - useEffect(() => { - if (metadataStatus === "success" && metadataSaveStarted) { - setMetadataSaveStarted(false); - dispatch(postVideoInformationWithWorkflow({ - segments: segments, - tracks: tracks, - workflow: [{ id: selectedWorkflowId }], - subtitles: prepareSubtitles(), - })); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [metadataStatus]); - - // Update based on current fetching status - let Icon = LuDatabase; - let spin = false; - if (workflowStatus === "failed" || metadataStatus === "failed") { - Icon = LuAlertCircle; - spin = false; - } else if (workflowStatus === "success" && metadataStatus === "success") { - Icon = LuCheck; - spin = false; - } else if (workflowStatus === "loading" || metadataStatus === "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 92f352c60..000000000 --- a/src/redux/workflowPostAndProcessSlice.ts +++ /dev/null @@ -1,64 +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, - 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 a25f0ddcb..034fbe10a 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, } ); return response; diff --git a/src/types.ts b/src/types.ts index 06232b9f3..4b6eb3949 100644 --- a/src/types.ts +++ b/src/types.ts @@ -68,10 +68,7 @@ export interface PostEditArgument { segments: Segment[] tracks: Track[] subtitles: SubtitlesFromOpencast[] -} - -export interface PostAndProcessEditArgument extends PostEditArgument{ - workflow: [{id: string}] + workflow?: [{id: string}] } // Use respective i18n keys as values