From 5e1564543660fc5270776bebb1ff6b126bd7afb9 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 27 Jun 2024 16:54:54 +0200 Subject: [PATCH 1/6] Make use of ProtoButton from appkit Replaces our home-brewed buttons with the basic ProtoButtons from appkit. There is arguably more work to be done on buttons, but this should serve as a good foundation for such future work. --- src/cssStyles.tsx | 5 +++- src/main/CuttingActions.tsx | 42 ++++++++++---------------- src/main/Discard.tsx | 13 ++++----- src/main/Finish.tsx | 25 ++++++---------- src/main/FinishMenu.tsx | 13 ++++----- src/main/Header.tsx | 4 ++- src/main/MainMenu.tsx | 13 ++++----- src/main/Save.tsx | 13 ++++----- src/main/SubtitleEditor.tsx | 26 +++++++---------- src/main/SubtitleListEditor.tsx | 9 +++--- src/main/SubtitleSelect.tsx | 35 +++++++++------------- src/main/TheEnd.tsx | 13 ++++----- src/main/Thumbnail.tsx | 25 +++++++--------- src/main/TrackSelection.tsx | 21 ++++++------- src/main/VideoControls.tsx | 47 ++++++++++++++---------------- src/main/WorkflowConfiguration.tsx | 13 ++++----- 16 files changed, 135 insertions(+), 182 deletions(-) diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index a96b39391..397372b28 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -31,6 +31,10 @@ export const globalStyle = (theme: Theme) => css({ // Makes the body span to the bottom of the page minHeight: "100vh", }, + // Some elements not inheriting fonts is a really confusing browser default. + "input, button, textarea, select": { + font: "inherit", + }, }); @@ -43,7 +47,6 @@ export const BREAKPOINT_MEDIUM = 650; */ export const basicButtonStyle = (theme: Theme) => css({ borderRadius: "5px", - cursor: "pointer", "&:hover": { backgroundColor: `${theme.button_color}`, color: `${theme.inverted_text}`, diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index b2612d70b..81f2922cf 100644 --- a/src/main/CuttingActions.tsx +++ b/src/main/CuttingActions.tsx @@ -30,6 +30,7 @@ import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; import { Slider } from "@mui/material"; import { useHotkeys } from "react-hotkeys-hook"; +import { ProtoButton } from "@opencast/appkit"; /** * Defines the different actions a user can perform while in cutting mode @@ -52,7 +53,7 @@ const CuttingActions: React.FC = () => { actionWithPayload?: ActionCreatorWithPayload | undefined, // eslint-disable-next-line @typescript-eslint/no-explicit-any payload?: any, - ref?: React.RefObject + ref?: React.RefObject ) => { if (action) { dispatch(action()); @@ -194,8 +195,6 @@ const CuttingActions: React.FC = () => { */ const cuttingActionButtonStyle = css({ padding: "16px", - // boxShadow: `${theme.boxShadow}`, - // background: `${theme.element_bg}` }); interface cuttingActionsButtonInterface { @@ -206,7 +205,7 @@ interface cuttingActionsButtonInterface { actionWithPayload: ActionCreatorWithPayload | undefined, // eslint-disable-next-line @typescript-eslint/no-explicit-any payload: any, - ref?: React.RefObject, + ref?: React.RefObject, ) => void, action: ActionCreatorWithoutPayload, actionWithPayload: ActionCreatorWithPayload | undefined, @@ -230,24 +229,20 @@ const CuttingActionsButton: React.FC = ({ tooltip, ariaLabelText, }) => { - const ref = React.useRef(null); + const ref = React.useRef(null); const theme = useTheme(); return ( -
actionHandler(action, actionWithPayload, payload, ref)} - onKeyDown={(event: React.KeyboardEvent) => { - if (event.key === " " || event.key === "Enter") { - actionHandler(action, actionWithPayload, payload); - } - }} + css={[basicButtonStyle(theme), cuttingActionButtonStyle]} > {actionName} -
+
); }; @@ -258,7 +253,7 @@ interface markAsDeleteButtonInterface { actionWithPayload: ActionCreatorWithPayload | undefined, // eslint-disable-next-line @typescript-eslint/no-explicit-any payload: any, - ref?: React.RefObject + ref?: React.RefObject ) => void, action: ActionCreatorWithoutPayload, hotKeyName: string, @@ -274,26 +269,21 @@ const MarkAsDeletedButton: React.FC = ({ }) => { const { t } = useTranslation(); const isCurrentSegmentAlive = useAppSelector(selectIsCurrentSegmentAlive); - const ref = React.useRef(null); + const ref = React.useRef(null); const theme = useTheme(); return ( -
actionHandler(action, undefined, undefined, ref)} - onKeyDown={(event: React.KeyboardEvent) => { - if (event.key === " " || event.key === "Enter") { - actionHandler(action, undefined, undefined); - } - }} + css={[basicButtonStyle(theme), cuttingActionButtonStyle]} > {isCurrentSegmentAlive ? : }
{isCurrentSegmentAlive ? t("cuttingActions.delete-button") : t("cuttingActions.restore-button")}
-
+
); }; @@ -304,7 +294,7 @@ interface ZoomSliderInterface { actionWithPayload: ActionCreatorWithPayload | undefined, // eslint-disable-next-line @typescript-eslint/no-explicit-any payload: any, - ref?: React.RefObject, + ref?: React.RefObject, ) => void, tooltip: string, ariaLabelText: string, diff --git a/src/main/Discard.tsx b/src/main/Discard.tsx index 168f59796..e6c51e1c5 100644 --- a/src/main/Discard.tsx +++ b/src/main/Discard.tsx @@ -13,6 +13,7 @@ import { PageButton } from "./Finish"; import { useTranslation } from "react-i18next"; import { useTheme } from "../themes"; +import { ProtoButton } from "@opencast/appkit"; /** * Shown if the user wishes to abort. @@ -61,17 +62,13 @@ const DiscardButton: React.FC = () => { }; return ( -
) => { - if (event.key === " " || event.key === "Enter") { - discard(); - } - }}> + css={[basicButtonStyle(theme), navigationButtonStyle(theme)]} + > {t("discard.confirm-button")} -
+ ); }; diff --git a/src/main/Finish.tsx b/src/main/Finish.tsx index 2905ad76a..cf38e19ca 100644 --- a/src/main/Finish.tsx +++ b/src/main/Finish.tsx @@ -18,6 +18,7 @@ import { selectPageNumber, setPageNumber } from "../redux/finishSlice"; import { useTheme } from "../themes"; import { settings } from "../config"; import { useTranslation } from "react-i18next"; +import { ProtoButton } from "@opencast/appkit"; /** * Displays a menu for selecting what should be done with the current changes @@ -86,17 +87,13 @@ export const PageButton: React.FC<{ }); return ( -
) => { - if (event.key === " " || event.key === "Enter") { - onPageChange(); - } - }}> + css={[basicButtonStyle(theme), pageButtonStyle]} + > {label} -
+ ); }; @@ -116,14 +113,10 @@ export const CallbackButton: React.FC = () => { return ( <> {settings.callbackUrl !== undefined && -
) => { - if (event.key === " " || event.key === "Enter") { - openCallbackUrl(); - } - }}> + css={[basicButtonStyle(theme), navigationButtonStyle(theme)]} + > {settings.callbackSystem ? @@ -131,7 +124,7 @@ export const CallbackButton: React.FC = () => { t("various.callback-button-generic") } -
+ } ); diff --git a/src/main/FinishMenu.tsx b/src/main/FinishMenu.tsx index e350e91be..fa8961566 100644 --- a/src/main/FinishMenu.tsx +++ b/src/main/FinishMenu.tsx @@ -11,6 +11,7 @@ import { setState, setPageNumber, finish } from "../redux/finishSlice"; import { useTranslation } from "react-i18next"; import { useTheme } from "../themes"; +import { ProtoButton } from "@opencast/appkit"; /** * Displays a menu for selecting what should be done with the current changes @@ -81,19 +82,15 @@ const FinishMenuButton: React.FC<{ Icon: IconType, stateName: finish["value"]; } }); return ( -
) => { - if (event.key === " " || event.key === "Enter") { - finish(); - } - }}> + css={[basicButtonStyle(theme), tileButtonStyle(theme)]} + >
{buttonString}
-
+ ); }; diff --git a/src/main/Header.tsx b/src/main/Header.tsx index c87811a9b..4ffac00b9 100644 --- a/src/main/Header.tsx +++ b/src/main/Header.tsx @@ -247,7 +247,9 @@ const HeaderButton = React.forwardRef( }); return ( - diff --git a/src/main/MainMenu.tsx b/src/main/MainMenu.tsx index dcbd193b2..3f8daece7 100644 --- a/src/main/MainMenu.tsx +++ b/src/main/MainMenu.tsx @@ -22,6 +22,7 @@ import { resetPostRequestState } from "../redux/workflowPostSlice"; import { setIsDisplayEditView } from "../redux/subtitleSlice"; import { useTheme } from "../themes"; +import { ProtoButton } from "@opencast/appkit"; /** * A container for selecting the functionality shown in the main part of the app @@ -147,15 +148,11 @@ export const MainMenuButton: React.FC = ({ }); return ( -
  • ) => { - if (event.key === "Enter") { - onMenuItemClicked(); - } - }} + css={[basicButtonStyle(theme), customCSS ? customCSS : mainMenuButtonStyle]} > = ({ height: "auto", }} /> {bottomText &&
    {bottomText}
    } -
  • +
    ); }; diff --git a/src/main/Save.tsx b/src/main/Save.tsx index cc78e929b..09a7b9ef2 100644 --- a/src/main/Save.tsx +++ b/src/main/Save.tsx @@ -32,6 +32,7 @@ import { import { serializeSubtitle } from "../util/utilityFunctions"; import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; +import { ProtoButton } from "@opencast/appkit"; /** * Shown if the user wishes to save. @@ -193,18 +194,14 @@ export const SaveButton: React.FC = () => { return ( -
    ) => { - if (event.key === " " || event.key === "Enter") { - save(); - } - }}> + css={[basicButtonStyle(theme), navigationButtonStyle(theme)]} + > {t("save.confirm-button")}
    {ariaSaveUpdate()}
    -
    +
    ); }; diff --git a/src/main/SubtitleEditor.tsx b/src/main/SubtitleEditor.tsx index aa3be72d8..f3dff94c9 100644 --- a/src/main/SubtitleEditor.tsx +++ b/src/main/SubtitleEditor.tsx @@ -21,7 +21,7 @@ import { parseSubtitle, serializeSubtitle } from "../util/utilityFunctions"; import { ThemedTooltip } from "./Tooltip"; import { titleStyle, titleStyleBold } from "../cssStyles"; import { generateButtonTitle } from "./SubtitleSelect"; -import { ConfirmationModal, ConfirmationModalHandle, Modal, ModalHandle } from "@opencast/appkit"; +import { ConfirmationModal, ConfirmationModalHandle, Modal, ModalHandle, ProtoButton } from "@opencast/appkit"; /** * Displays an editor view for a selected subtitle file @@ -194,13 +194,13 @@ const DownloadButton: React.FC = () => { return ( -
    downloadSubtitles()} + css={[basicButtonStyle(theme), subtitleButtonStyle(theme)]} > {t("subtitles.downloadButton-title")} -
    +
    ); }; @@ -267,13 +267,13 @@ const UploadButton: React.FC<{ return ( <> -
    modalRef.current?.open()} + css={[basicButtonStyle(theme), subtitleButtonStyle(theme)]} > {t("subtitles.uploadButton-title")} -
    +
    {/* Hidden input field for upload */} { return ( -
    dispatch(setIsDisplayEditView(false))} - onKeyDown={(event: React.KeyboardEvent) => { - if (event.key === " " || event.key === "Enter") { - dispatch(setIsDisplayEditView(false)); - } - }}> + css={[basicButtonStyle(theme), subtitleButtonStyle(theme)]} + > {t("subtitles.backButton")} -
    +
    ); }; diff --git a/src/main/SubtitleListEditor.tsx b/src/main/SubtitleListEditor.tsx index 84caabb1c..942e68dea 100644 --- a/src/main/SubtitleListEditor.tsx +++ b/src/main/SubtitleListEditor.tsx @@ -32,7 +32,7 @@ import AutoSizer from "react-virtualized-auto-sizer"; import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; import { useHotkeys } from "react-hotkeys-hook"; -import { useColorScheme } from "@opencast/appkit"; +import { ProtoButton, useColorScheme } from "@opencast/appkit"; /** * Displays everything needed to edit subtitles @@ -482,15 +482,14 @@ const FunctionButton: React.FC<{ return ( -
    -
    +
    ); }; diff --git a/src/main/SubtitleSelect.tsx b/src/main/SubtitleSelect.tsx index c415a8670..115e9f6f7 100644 --- a/src/main/SubtitleSelect.tsx +++ b/src/main/SubtitleSelect.tsx @@ -22,6 +22,7 @@ import { ThemedTooltip } from "./Tooltip"; import { languageCodeToName } from "../util/utilityFunctions"; import { v4 as uuidv4 } from "uuid"; import { TFunction } from "i18next"; +import { ProtoButton } from "@opencast/appkit"; /** * Displays buttons that allow the user to select the subtitle they want to edit @@ -153,6 +154,11 @@ const SubtitleSelectButton: React.FC<{ const theme = useTheme(); const dispatch = useAppDispatch(); + const onClickHandler = () => { + dispatch(setIsDisplayEditView(true)); + dispatch(setSelectedSubtitleId(id)); + }; + const flagStyle = css({ fontSize: "2.5em", overflow: "hidden", @@ -175,22 +181,14 @@ const SubtitleSelectButton: React.FC<{ return ( -
    { - dispatch(setIsDisplayEditView(true)); - dispatch(setSelectedSubtitleId(id)); - }} - onKeyDown={(event: React.KeyboardEvent) => { - if (event.key === " " || event.key === "Enter") { - dispatch(setIsDisplayEditView(true)); - dispatch(setSelectedSubtitleId(id)); - } - }}> + onClick={onClickHandler} + css={[basicButtonStyle(theme), tileButtonStyle(theme)]} + > {icon &&
    {icon}
    }
    {title ?? t("subtitles.generic") + " " + id}
    -
    +
    ); }; @@ -270,16 +268,11 @@ const SubtitleAddButton: React.FC<{ return ( -
    setIsPlusDisplay(false)} - onKeyDown={(event: React.KeyboardEvent) => { - if (event.key === " " || event.key === "Enter") { - setIsPlusDisplay(false); - } - }} + css={[basicButtonStyle(theme), tileButtonStyle(theme), !isPlusDisplay && disableButtonAnimation]} >
    )} /> -
    +
    ); }; diff --git a/src/main/TheEnd.tsx b/src/main/TheEnd.tsx index a8ab3e204..e5568534c 100644 --- a/src/main/TheEnd.tsx +++ b/src/main/TheEnd.tsx @@ -12,6 +12,7 @@ import { useTranslation } from "react-i18next"; import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; import { CallbackButton } from "./Finish"; +import { ProtoButton } from "@opencast/appkit"; /** * This page is to be displayed when the user is "done" with the editor @@ -72,16 +73,12 @@ const StartOverButton: React.FC = () => { return ( -
    ) => { - if (event.key === " " || event.key === "Enter") { - reloadPage(); - } - }}> + css={[basicButtonStyle(theme), navigationButtonStyle(theme)]} + > {t("theEnd.startOver-button")} -
    +
    ); }; diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index 7d2d5c710..d4c5e2ce0 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -38,6 +38,7 @@ import { import { ThemedTooltip } from "./Tooltip"; import VideoPlayers, { VideoPlayerForwardRef } from "./VideoPlayers"; import VideoControls from "./VideoControls"; +import { ProtoButton } from "@opencast/appkit"; /** @@ -434,7 +435,7 @@ const ThumbnailButton: React.FC<{ active: boolean, }> = ({ handler, text, tooltipText, ariaLabel, Icon, active }) => { const theme = useTheme(); - const ref = React.useRef(null); + const ref = React.useRef(null); const clickHandler = () => { if (active) { handler(); } @@ -448,16 +449,16 @@ const ThumbnailButton: React.FC<{ return ( -
    {text} -
    +
    ); }; @@ -501,20 +502,16 @@ const AffectAllRow: React.FC<{ return (
    -
    { generateAll(); }} - onKeyDown={(event: React.KeyboardEvent) => { - if (event.key === " " || event.key === "Enter") { - generateAll(); - } - }} + css={[basicButtonStyle(theme), buttonStyle]} > {t("thumbnail.buttonGenerateAll")} -
    +
    ); diff --git a/src/main/TrackSelection.tsx b/src/main/TrackSelection.tsx index d26985f0c..76458caa6 100644 --- a/src/main/TrackSelection.tsx +++ b/src/main/TrackSelection.tsx @@ -19,8 +19,9 @@ import { } from "../cssStyles"; import { useTranslation } from "react-i18next"; -import { useTheme } from "../themes"; +import { Theme, useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; +import { ProtoButton } from "@opencast/appkit"; /** * Creates the track selection. @@ -189,7 +190,7 @@ const SelectButton: React.FC = ({ handler, text, Icon, to const theme = useTheme(); - const buttonStyle = [ + const buttonStyle = (theme: Theme) => [ active ? basicButtonStyle(theme) : deactivatedButtonStyle, { padding: "16px", @@ -197,7 +198,8 @@ const SelectButton: React.FC = ({ handler, text, Icon, to boxShadow: "", background: `${theme.element_bg}`, textWrap: "nowrap", - }]; + }, + ]; const clickHandler = () => { if (active) { handler(); } @@ -214,17 +216,16 @@ const SelectButton: React.FC = ({ handler, text, Icon, to return ( -
    + onKeyDown={keyHandler} + css={buttonStyle(theme)} + > {text} -
    +
    ); }; diff --git a/src/main/VideoControls.tsx b/src/main/VideoControls.tsx index c5c2c70ec..9051686d4 100644 --- a/src/main/VideoControls.tsx +++ b/src/main/VideoControls.tsx @@ -23,6 +23,7 @@ import { ThemedTooltip } from "./Tooltip"; import { Theme, useTheme } from "../themes"; import { useHotkeys } from "react-hotkeys-hook"; import { Slider } from "@mui/material"; +import { ProtoButton } from "@opencast/appkit"; /** * Contains controls for manipulating multiple video players at once @@ -231,19 +232,15 @@ const PlayButton: React.FC<{ return ( -
    -
    { switchIsPlaying(); }} - onKeyDown={(event: React.KeyboardEvent) => { - if (event.key === "Enter") { // "Space" is handled by global key - switchIsPlaying(); - } - }}> - {isPlaying ? : } -
    -
    + { switchIsPlaying(); }} + css={[basicButtonStyle(theme), playButtonStyle]} + > + {isPlaying ? : } +
    ); }; @@ -316,17 +313,14 @@ const NextButton: React.FC<{ return ( -
    { - if (event.key === "Enter") { - jumpToNext(); - } - }}> + css={[basicButtonStyle(theme)]} + > -
    +
    ); }; @@ -452,12 +446,15 @@ const VolumeSlider: React.FC<{ return (
    -
    + aria-pressed={isMuted} + aria-hidden={false} + onClick={switchIsMuted} + css={[basicButtonStyle(theme)]} + > {isMuted ? : } -
    +
    = ({ text }) => { }); return ( -
    ) => { - if (event.key === " " || event.key === "Enter") { - saveAndProcess(); - } - }}> + css={[basicButtonStyle(theme), saveButtonStyle]} + > {text} -
    + ); }; From 553ffededa260c7892c56449d319260b65788724 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 3 Jul 2024 10:35:00 +0200 Subject: [PATCH 2/6] Fix playwright tests for ProtoButton --- tests/metadata.test.ts | 2 +- tests/navigation.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/metadata.test.ts b/tests/metadata.test.ts index 41af1a00a..79cd4c9c9 100644 --- a/tests/metadata.test.ts +++ b/tests/metadata.test.ts @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; test.beforeEach(async ({ page, baseURL }) => { await page.goto(baseURL); - await page.click('li[role="menuitem"]:has-text("Metadata")'); + await page.click('button[role="menuitem"]:has-text("Metadata")'); }); test.describe('Test Metadata-Page', () => { diff --git a/tests/navigation.test.ts b/tests/navigation.test.ts index 45cf56fc9..ef7529437 100644 --- a/tests/navigation.test.ts +++ b/tests/navigation.test.ts @@ -7,12 +7,12 @@ test('Test: Navigation', async ({ page, baseURL }) => { await expect(page).toHaveTitle("Opencast Editor"); // checks if Navbar on left has 4 elements - const length = await page.locator('#root > div > div > nav > li').count(); + const length = await page.locator('#root > div > div > nav > button').count(); expect(length >= 2).toBeTruthy(); // Navigation to Finish - await page.click('li[role="menuitem"]:has-text("Finish")'); + await page.click('button[role="menuitem"]:has-text("Finish")'); // Navigation to Cutting - await page.click('li[role="menuitem"]:has-text("Cutting")'); + await page.click('button[role="menuitem"]:has-text("Cutting")'); }); From 996af564faa8d657a11605ff22581fc29d8d2baf Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 9 Jul 2024 16:58:10 +0200 Subject: [PATCH 3/6] Add typing to async thunks Adds typescript to createAsyncThunk from redux toolkit. This properly types our state in those methods, should we need it. --- src/redux/createAsyncThunkWithTypes.ts | 14 ++++++++++++++ src/redux/metadataSlice.ts | 10 +++++----- src/redux/subtitleSlice.ts | 9 ++++----- src/redux/videoSlice.ts | 5 +++-- src/redux/workflowPostAndProcessSlice.ts | 5 +++-- src/redux/workflowPostSlice.ts | 5 +++-- 6 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 src/redux/createAsyncThunkWithTypes.ts diff --git a/src/redux/createAsyncThunkWithTypes.ts b/src/redux/createAsyncThunkWithTypes.ts new file mode 100644 index 000000000..cecaa5091 --- /dev/null +++ b/src/redux/createAsyncThunkWithTypes.ts @@ -0,0 +1,14 @@ +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { AppDispatch, RootState } from "./store"; + +/** + * Use instead of createAsyncThunk to provide basic typing to all async thunks + * + * Thematically this belongs in `store.ts`. However, this causes + * "Cannot access 'createAsyncThunk' before initialization", + * so this has to be moved into it's own file. + */ +export const createAppAsyncThunk = createAsyncThunk.withTypes<{ + state: RootState + dispatch: AppDispatch +}>(); diff --git a/src/redux/metadataSlice.ts b/src/redux/metadataSlice.ts index 4d7d27678..21b2e21f8 100644 --- a/src/redux/metadataSlice.ts +++ b/src/redux/metadataSlice.ts @@ -1,8 +1,9 @@ -import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { client } from "../util/client"; import { httpRequestState } from "../types"; import { settings } from "../config"; +import { createAppAsyncThunk } from "./createAsyncThunkWithTypes"; export interface Catalog { fields: MetadataField[], @@ -69,7 +70,7 @@ const initialState: metadata & httpRequestState & postRequestState = { postErrorReason: "unknown", }; -export const fetchMetadata = createAsyncThunk("metadata/fetchMetadata", async () => { +export const fetchMetadata = createAppAsyncThunk("metadata/fetchMetadata", async () => { if (!settings.id) { throw new Error("Missing media package identifier"); } @@ -78,13 +79,12 @@ export const fetchMetadata = createAsyncThunk("metadata/fetchMetadata", async () return JSON.parse(response); }); -export const postMetadata = createAsyncThunk("metadata/postMetadata", async (_, { getState }) => { +export const postMetadata = createAppAsyncThunk("metadata/postMetadata", async (_, { getState }) => { if (!settings.id) { throw new Error("Missing media package identifier"); } - // TODO: Get only metadataState instead of all states - const allStates = getState() as { metadataState: { catalogs: metadata["catalogs"]; }; }; + const allStates = getState(); await client.post(`${settings.opencast.url}/editor/${settings.id}/metadata.json`, allStates.metadataState.catalogs diff --git a/src/redux/subtitleSlice.ts b/src/redux/subtitleSlice.ts index 3fff70fc6..6c7bf580f 100644 --- a/src/redux/subtitleSlice.ts +++ b/src/redux/subtitleSlice.ts @@ -1,8 +1,7 @@ import { Segment, SubtitleCue, SubtitlesInEditor } from "./../types"; -import { createAsyncThunk, createSlice, nanoid, PayloadAction } from "@reduxjs/toolkit"; +import { createSlice, nanoid, PayloadAction } from "@reduxjs/toolkit"; import { roundToDecimalPlace } from "../util/utilityFunctions"; -import type { RootState } from "../redux/store"; -import { video } from "./videoSlice"; +import { createAppAsyncThunk } from "./createAsyncThunkWithTypes"; export interface subtitle { isDisplayEditView: boolean; // Should the edit view be displayed @@ -250,7 +249,7 @@ export const { * Will grab the state from videoState to skip past deleted segment if preview * mode is active. */ -export const setCurrentlyAtAndTriggerPreview = createAsyncThunk("subtitleState/setCurrentlyAtAndTriggerPreview", +export const setCurrentlyAtAndTriggerPreview = createAppAsyncThunk("subtitleState/setCurrentlyAtAndTriggerPreview", async (milliseconds: number, { getState, dispatch }) => { milliseconds = roundToDecimalPlace(milliseconds, 0); @@ -258,7 +257,7 @@ export const setCurrentlyAtAndTriggerPreview = createAsyncThunk("subtitleState/s milliseconds = 0; } - const allStates = getState() as { videoState: video, subtitleState: subtitle; }; + const allStates = getState(); const segments: Segment[] = allStates.videoState.segments; let triggered = false; diff --git a/src/redux/videoSlice.ts b/src/redux/videoSlice.ts index e8884c8eb..0f0a06954 100644 --- a/src/redux/videoSlice.ts +++ b/src/redux/videoSlice.ts @@ -1,10 +1,11 @@ import { clamp } from "lodash"; -import { createSlice, nanoid, createAsyncThunk, PayloadAction, createSelector } from "@reduxjs/toolkit"; +import { createSlice, nanoid, PayloadAction, createSelector } from "@reduxjs/toolkit"; import { client } from "../util/client"; import { Segment, httpRequestState, Track, Workflow, SubtitlesFromOpencast } from "../types"; import { roundToDecimalPlace } from "../util/utilityFunctions"; import { settings } from "../config"; +import { createAppAsyncThunk } from "./createAsyncThunkWithTypes"; export interface video { isPlaying: boolean, // Are videos currently playing? @@ -81,7 +82,7 @@ export const initialState: video & httpRequestState = { errorReason: "unknown", }; -export const fetchVideoInformation = createAsyncThunk("video/fetchVideoInformation", async () => { +export const fetchVideoInformation = createAppAsyncThunk("video/fetchVideoInformation", async () => { if (!settings.id) { throw new Error("Missing media package identifier"); } diff --git a/src/redux/workflowPostAndProcessSlice.ts b/src/redux/workflowPostAndProcessSlice.ts index 92f352c60..8c6274f34 100644 --- a/src/redux/workflowPostAndProcessSlice.ts +++ b/src/redux/workflowPostAndProcessSlice.ts @@ -1,9 +1,10 @@ -import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; +import { createSlice } from "@reduxjs/toolkit"; import { client } from "../util/client"; import { PostAndProcessEditArgument, httpRequestState } from "../types"; import { convertSegments } from "./workflowPostSlice"; import { settings } from "../config"; +import { createAppAsyncThunk } from "./createAsyncThunkWithTypes"; const initialState: httpRequestState = { status: "idle", @@ -12,7 +13,7 @@ const initialState: httpRequestState = { }; export const postVideoInformationWithWorkflow = - createAsyncThunk("video/postVideoInformationWithWorkflow", async (argument: PostAndProcessEditArgument) => { + createAppAsyncThunk("video/postVideoInformationWithWorkflow", async (argument: PostAndProcessEditArgument) => { if (!settings.id) { throw new Error("Missing media package identifier"); } diff --git a/src/redux/workflowPostSlice.ts b/src/redux/workflowPostSlice.ts index a25f0ddcb..08120186c 100644 --- a/src/redux/workflowPostSlice.ts +++ b/src/redux/workflowPostSlice.ts @@ -1,7 +1,8 @@ -import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; +import { createSlice } from "@reduxjs/toolkit"; import { client } from "../util/client"; import { Segment, PostEditArgument, httpRequestState } from "../types"; import { settings } from "../config"; +import { createAppAsyncThunk } from "./createAsyncThunkWithTypes"; const initialState: httpRequestState = { status: "idle", @@ -10,7 +11,7 @@ const initialState: httpRequestState = { }; export const postVideoInformation = - createAsyncThunk("video/postVideoInformation", async (argument: PostEditArgument) => { + createAppAsyncThunk("video/postVideoInformation", async (argument: PostEditArgument) => { if (!settings.id) { throw new Error("Missing media package id"); } From 493fe13d048543a9dab56a34faeaccf61cf01259 Mon Sep 17 00:00:00 2001 From: Gregor Eichelberger Date: Mon, 2 Dec 2024 22:59:37 +0100 Subject: [PATCH 4/6] Add subtitle delete button This adds a subtitle delete button to the subtitle editor, which allows the deletion of newly created subtitles or already existing ones. --- src/i18n/locales/en-US.json | 4 +++ src/main/Save.tsx | 20 ++++++--------- src/main/SubtitleEditor.tsx | 49 +++++++++++++++++++++++++++++++++++-- src/main/SubtitleSelect.tsx | 13 ++++++---- src/redux/subtitleSlice.ts | 7 ++++-- src/types.ts | 1 + 6 files changed, 72 insertions(+), 22 deletions(-) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index c74eeb1d0..accb088a3 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -260,6 +260,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/main/Save.tsx b/src/main/Save.tsx index 7e9323674..88da45893 100644 --- a/src/main/Save.tsx +++ b/src/main/Save.tsx @@ -144,19 +144,13 @@ export const SaveButton: React.FC<{ return t("save.success-tooltip-aria"); } }; - - 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 prepareSubtitles = () => + Object.entries(subtitles).map(([id, { deleted, cues, tags }]) => ({ + id, + subtitle: deleted ? "" : serializeSubtitle(cues), + tags: deleted ? [] : tags, + deleted, + })); const save = () => { dispatch(postVideoInformation({ diff --git a/src/main/SubtitleEditor.tsx b/src/main/SubtitleEditor.tsx index aa3be72d8..87ece4ed5 100644 --- a/src/main/SubtitleEditor.tsx +++ b/src/main/SubtitleEditor.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { css } from "@emotion/react"; import { basicButtonStyle } from "../cssStyles"; -import { LuChevronLeft, LuDownload, LuUpload } from "react-icons/lu"; +import { LuChevronLeft, LuDownload, LuTrash2, LuUpload } from "react-icons/lu"; import { selectSubtitlesFromOpencastById, } from "../redux/videoSlice"; @@ -12,6 +12,7 @@ import { selectSelectedSubtitleById, selectSelectedSubtitleId, setSubtitle, + removeSubtitle, } from "../redux/subtitleSlice"; import SubtitleVideoArea from "./SubtitleVideoArea"; import SubtitleTimeline from "./SubtitleTimeline"; @@ -60,7 +61,7 @@ const SubtitleEditor: React.FC = () => { // Or create a new subtitle instead } else if (subtitle?.cues === undefined && captionTrack === undefined && selectedId) { // Create an empty subtitle - dispatch(setSubtitle({ identifier: selectedId, subtitles: { cues: [], tags: [] } })); + dispatch(setSubtitle({ identifier: selectedId, subtitles: { cues: [], tags: [], deleted: false } })); } }, [dispatch, captionTrack, subtitle, selectedId]); @@ -135,6 +136,7 @@ const SubtitleEditor: React.FC = () => { {t("subtitles.editTitle", { title: getTitle() })}
    + css({ background: `${theme.element_bg}`, }); +const DeleteButton: React.FC = () => { + + const { t } = useTranslation(); + const theme = useTheme(); + const dispatch = useAppDispatch(); + + const selectedId = useAppSelector(selectSelectedSubtitleId); + + // Modal Ref + const modalRef = React.useRef(null); + + return ( + <> + +
    modalRef.current?.open()} + > + + {t("subtitles.deleteButton-title")} +
    +
    + {/* Hidden input field for upload */} + { + modalRef.current?.done(); + dispatch(removeSubtitle({ identifier: selectedId })); + dispatch(setIsDisplayEditView(false)); + }} + ref={modalRef} + text={{ + cancel: t("modal.cancel"), + close: t("modal.close"), + areYouSure: t("modal.areYouSure"), + }} + > + {t("subtitles.deleteButton-warning")} + + + ); +}; const DownloadButton: React.FC = () => { const subtitle = useAppSelector(selectSelectedSubtitleById); diff --git a/src/main/SubtitleSelect.tsx b/src/main/SubtitleSelect.tsx index c415a8670..1d3b29243 100644 --- a/src/main/SubtitleSelect.tsx +++ b/src/main/SubtitleSelect.tsx @@ -32,7 +32,7 @@ const SubtitleSelect: React.FC = () => { const subtitlesFromOpencast = useAppSelector(selectSubtitlesFromOpencast); // track objects received from Opencast const subtitles = useAppSelector(selectSubtitles); // parsed subtitles stored in redux - const [displaySubtitles, setDisplaySubtitles] = useState<{ id: string, tags: string[]; }[]>([]); + const [displaySubtitles, setDisplaySubtitles] = useState<{ id: string, tags: string[], deleted: boolean; }[]>([]); const [canBeAddedSubtitles, setCanBeAddedSubtitles] = useState<{ id: string, tags: string[]; }[]>([]); // Update the collections for the select and add buttons @@ -43,12 +43,12 @@ const SubtitleSelect: React.FC = () => { let existingSubtitles = subtitlesFromOpencast .filter(track => !subtitles[track.id]) .map(track => { - return { id: track.id, tags: track.tags }; + return { id: track.id, tags: track.tags, deleted: false }; }); existingSubtitles = Object.entries(subtitles) .map(track => { - return { id: track[0], tags: track[1].tags }; + return { id: track[0], tags: track[1].tags, deleted: track[1].deleted }; }) .concat(existingSubtitles); @@ -57,7 +57,7 @@ const SubtitleSelect: React.FC = () => { const subtitlesFromOpencastLangs = subtitlesFromOpencast .reduce((result: { id: string, lang: string; }[], track) => { const lang = track.tags.find(e => e.startsWith("lang:")); - if (lang) { + if (lang && !subtitles[track.id]?.deleted) { result.push({ id: track.id, lang: lang.split(":")[1].trim() }); } return result; @@ -66,7 +66,7 @@ const SubtitleSelect: React.FC = () => { const subtitlesLangs = Object.entries(subtitles) .reduce((result: { id: string, lang: string; }[], track) => { const lang = track[1].tags.find(e => e.startsWith("lang:")); - if (lang) { + if (lang && !subtitles[track[0]]?.deleted) { result.push({ id: track[0], lang: lang.split(":")[1].trim() }); } return result; @@ -112,6 +112,9 @@ const SubtitleSelect: React.FC = () => { } for (const subtitle of displaySubtitles) { + if (subtitle.deleted) { + continue; + } let lang = subtitle.tags.find(e => e.startsWith("lang:")); lang = lang ? lang.split(":")[1].trim() : undefined; const icon = lang ? ((settings.subtitles || {}).icons || {})[lang] : undefined; diff --git a/src/redux/subtitleSlice.ts b/src/redux/subtitleSlice.ts index 3fff70fc6..770edbe7a 100644 --- a/src/redux/subtitleSlice.ts +++ b/src/redux/subtitleSlice.ts @@ -1,7 +1,6 @@ import { Segment, SubtitleCue, SubtitlesInEditor } from "./../types"; import { createAsyncThunk, createSlice, nanoid, PayloadAction } from "@reduxjs/toolkit"; import { roundToDecimalPlace } from "../util/utilityFunctions"; -import type { RootState } from "../redux/store"; import { video } from "./videoSlice"; export interface subtitle { @@ -78,6 +77,10 @@ export const subtitleSlice = createSlice({ setSubtitle: (state, action: PayloadAction<{ identifier: string, subtitles: SubtitlesInEditor; }>) => { state.subtitles[action.payload.identifier] = action.payload.subtitles; }, + removeSubtitle: (state, action: PayloadAction<{ identifier: string; }>) => { + state.subtitles[action.payload.identifier].deleted = true; + state.hasChanges = true; + }, setCueAtIndex: (state, action: PayloadAction<{ identifier: string, cueIndex: number, newCue: SubtitleCue; }>) => { if (action.payload.cueIndex < 0 || action.payload.cueIndex >= state.subtitles[action.payload.identifier].cues.length) { @@ -223,7 +226,7 @@ const sortSubtitle = (state: subtitle, identifier: string) => { // Export Actions export const { setIsDisplayEditView, setIsPlaying, setIsPlayPreview, setPreviewTriggered, setCurrentlyAt, - setCurrentlyAtInSeconds, setClickTriggered, setSubtitle, setCueAtIndex, addCueAtIndex, removeCue, + setCurrentlyAtInSeconds, setClickTriggered, setSubtitle, removeSubtitle, setCueAtIndex, addCueAtIndex, removeCue, setSelectedSubtitleId, setFocusSegmentTriggered, setFocusSegmentId, setFocusSegmentTriggered2, setFocusToSegmentAboveId, setFocusToSegmentBelowId, setAspectRatio, setHasChanges } = subtitleSlice.actions; diff --git a/src/types.ts b/src/types.ts index 1ee6ff3cd..600ee8f52 100644 --- a/src/types.ts +++ b/src/types.ts @@ -43,6 +43,7 @@ export interface SubtitlesFromOpencast { export interface SubtitlesInEditor { cues: SubtitleCue[], tags: string[], + deleted: boolean, } export interface SubtitleCue { From 6ace5c0eb9966d2bec25ff5a744448adc8c9f8fa Mon Sep 17 00:00:00 2001 From: Gregor Eichelberger Date: Thu, 5 Dec 2024 12:14:44 +0100 Subject: [PATCH 5/6] Delete subtitle on save and process Change prepareSubtitles for WorklfowConfiguration to honor deleted subtitles. --- src/main/Save.tsx | 1 + src/main/SubtitleEditor.tsx | 2 +- src/main/SubtitleSelect.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/Save.tsx b/src/main/Save.tsx index 88da45893..75ecc09e9 100644 --- a/src/main/Save.tsx +++ b/src/main/Save.tsx @@ -144,6 +144,7 @@ export const SaveButton: React.FC<{ return t("save.success-tooltip-aria"); } }; + const prepareSubtitles = () => Object.entries(subtitles).map(([id, { deleted, cues, tags }]) => ({ id, diff --git a/src/main/SubtitleEditor.tsx b/src/main/SubtitleEditor.tsx index 87ece4ed5..ac3be666c 100644 --- a/src/main/SubtitleEditor.tsx +++ b/src/main/SubtitleEditor.tsx @@ -48,7 +48,7 @@ const SubtitleEditor: React.FC = () => { try { dispatch(setSubtitle({ identifier: selectedId, - subtitles: { cues: parseSubtitle(captionTrack.subtitle), tags: captionTrack.tags }, + subtitles: { cues: parseSubtitle(captionTrack.subtitle), tags: captionTrack.tags, deleted: false }, })); } catch (error) { if (error instanceof Error) { diff --git a/src/main/SubtitleSelect.tsx b/src/main/SubtitleSelect.tsx index 1d3b29243..173919455 100644 --- a/src/main/SubtitleSelect.tsx +++ b/src/main/SubtitleSelect.tsx @@ -235,7 +235,7 @@ const SubtitleAddButton: React.FC<{ const id = values.selectedSubtitle; const relatedSubtitle = subtitlesForDropdown.find(tag => tag.id === id); const tags = relatedSubtitle ? relatedSubtitle.tags : []; - dispatch(setSubtitle({ identifier: id, subtitles: { cues: [], tags: tags } })); + dispatch(setSubtitle({ identifier: id, subtitles: { cues: [], tags: tags, delete: false } })); // Reset setIsPlusDisplay(true); From 58cee66cf4abaace484152d402bcefd2f1f9a0e0 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 16 Dec 2024 05:19:01 +0000 Subject: [PATCH 6/6] Automatically update translation keys --- src/i18n/locales/am-ET.json | 4 ++++ src/i18n/locales/cs-CZ.json | 4 ++++ src/i18n/locales/de-AT.json | 4 ++++ src/i18n/locales/de-DE.json | 4 ++++ src/i18n/locales/el-GR.json | 4 ++++ src/i18n/locales/es-ES.json | 4 ++++ src/i18n/locales/fr-FR.json | 4 ++++ src/i18n/locales/gl-ES.json | 4 ++++ src/i18n/locales/he-IL.json | 4 ++++ src/i18n/locales/it-IT.json | 4 ++++ src/i18n/locales/nl-NL.json | 4 ++++ src/i18n/locales/pl-PL.json | 4 ++++ src/i18n/locales/sl-SI.json | 4 ++++ src/i18n/locales/sv-SE.json | 4 ++++ src/i18n/locales/tr-TR.json | 4 ++++ src/i18n/locales/zh-CN.json | 4 ++++ src/i18n/locales/zh-TW.json | 4 ++++ 17 files changed, 68 insertions(+) diff --git a/src/i18n/locales/am-ET.json b/src/i18n/locales/am-ET.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/am-ET.json +++ b/src/i18n/locales/am-ET.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/cs-CZ.json b/src/i18n/locales/cs-CZ.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/cs-CZ.json +++ b/src/i18n/locales/cs-CZ.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/de-AT.json b/src/i18n/locales/de-AT.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/de-AT.json +++ b/src/i18n/locales/de-AT.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/de-DE.json b/src/i18n/locales/de-DE.json index 401e5d87e..804184f7d 100644 --- a/src/i18n/locales/de-DE.json +++ b/src/i18n/locales/de-DE.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Wähle eine Sprache", "backButton": "Zurück", "backButton-tooltip": "Zurück zur Untertitelauswahl", + "deleteButton-title": "Löschen", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Achtung!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Herunterladen", "downloadButton-tooltip": "Untertitel als vtt-Datei herunterladen", "uploadButton-title": "Hochladen", diff --git a/src/i18n/locales/el-GR.json b/src/i18n/locales/el-GR.json index 0d28cef94..b50849f68 100644 --- a/src/i18n/locales/el-GR.json +++ b/src/i18n/locales/el-GR.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Επιλέξτε μια γλώσσα", "backButton": "Επιστροφή", "backButton-tooltip": "Επιστροφή στην επιλογή υποτίτλων", + "deleteButton-title": "Διαγραφή", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Προσοχή!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Λήψη", "downloadButton-tooltip": "Λήψη υποτίτλων ως αρχείο vtt", "uploadButton-title": "Ανέβασμα", diff --git a/src/i18n/locales/es-ES.json b/src/i18n/locales/es-ES.json index f21459785..517784bbc 100644 --- a/src/i18n/locales/es-ES.json +++ b/src/i18n/locales/es-ES.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Seleccione un idioma", "backButton": "Atras", "backButton-tooltip": "Volver a la selección de subtítulos", + "deleteButton-title": "Borrar", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Descargar", "downloadButton-tooltip": "Descargar subtítulo como un archivo vtt", "uploadButton-title": "Cargar", diff --git a/src/i18n/locales/fr-FR.json b/src/i18n/locales/fr-FR.json index cb3b13125..f82629752 100644 --- a/src/i18n/locales/fr-FR.json +++ b/src/i18n/locales/fr-FR.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Supprimer", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/gl-ES.json b/src/i18n/locales/gl-ES.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/gl-ES.json +++ b/src/i18n/locales/gl-ES.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/he-IL.json b/src/i18n/locales/he-IL.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/he-IL.json +++ b/src/i18n/locales/he-IL.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/it-IT.json b/src/i18n/locales/it-IT.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/it-IT.json +++ b/src/i18n/locales/it-IT.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/nl-NL.json b/src/i18n/locales/nl-NL.json index 505d23202..572b59164 100644 --- a/src/i18n/locales/nl-NL.json +++ b/src/i18n/locales/nl-NL.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Verwijder", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/pl-PL.json b/src/i18n/locales/pl-PL.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/pl-PL.json +++ b/src/i18n/locales/pl-PL.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/sl-SI.json b/src/i18n/locales/sl-SI.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/sl-SI.json +++ b/src/i18n/locales/sl-SI.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/sv-SE.json b/src/i18n/locales/sv-SE.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/sv-SE.json +++ b/src/i18n/locales/sv-SE.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/tr-TR.json b/src/i18n/locales/tr-TR.json index 06c783313..2caa10b7e 100644 --- a/src/i18n/locales/tr-TR.json +++ b/src/i18n/locales/tr-TR.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 1355632bc..a819845cd 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "Delete", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 369ac72a0..3bca6d164 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -243,6 +243,10 @@ "createSubtitleDropdown-label": "Pick a language", "backButton": "Back", "backButton-tooltip": "Return to subtitle selection", + "deleteButton-title": "刪除", + "deleteButton-tooltip": "Delete subtitle", + "deleteButton-warning-header": "Caution!", + "deleteButton-warning": "You will remove the current subtitle. This cannot be undone. Are you sure?", "downloadButton-title": "Download", "downloadButton-tooltip": "Download subtitle as vtt file", "uploadButton-title": "Upload",