diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 02e35baca..1392b59cf 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -34,6 +34,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", + }, }); @@ -46,7 +50,6 @@ export const BREAKPOINTS = APPKIT_CONFIG.breakpoints; */ export const basicButtonStyle = (theme: Theme) => css({ borderRadius: "5px", - cursor: "pointer", "&:hover": { backgroundColor: `${theme.button_color}`, color: `${theme.inverted_text}`, 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/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/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", diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index f65977aa5..90f5d3894 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()); @@ -196,8 +197,6 @@ const CuttingActions: React.FC = () => { */ const cuttingActionButtonStyle = css({ padding: "16px", - // boxShadow: `${theme.boxShadow}`, - // background: `${theme.element_bg}` }); interface cuttingActionsButtonInterface { @@ -208,7 +207,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, @@ -232,24 +231,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} -
+
); }; @@ -260,7 +255,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, @@ -276,28 +271,23 @@ 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")} -
+
); }; @@ -308,7 +298,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 93650348d..71d8835be 100644 --- a/src/main/Discard.tsx +++ b/src/main/Discard.tsx @@ -12,6 +12,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. @@ -58,17 +59,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 0f93dd44d..a6ce9b317 100644 --- a/src/main/Finish.tsx +++ b/src/main/Finish.tsx @@ -18,6 +18,7 @@ import { selectFinishState, selectPageNumber, setPageNumber } from "../redux/fin 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 @@ -89,17 +90,13 @@ export const PageButton: React.FC<{ }); return ( -
) => { - if (event.key === " " || event.key === "Enter") { - onPageChange(); - } - }}> + css={[basicButtonStyle(theme), pageButtonStyle]} + > {label} -
+ ); }; @@ -119,14 +116,10 @@ export const CallbackButton: React.FC = () => { return ( <> {settings.callbackUrl !== undefined && -
) => { - if (event.key === " " || event.key === "Enter") { - openCallbackUrl(); - } - }}> + css={[basicButtonStyle(theme), navigationButtonStyle(theme)]} + > {settings.callbackSystem ? @@ -134,7 +127,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 0035c07be..f5a10c062 100644 --- a/src/main/Header.tsx +++ b/src/main/Header.tsx @@ -275,7 +275,9 @@ const HeaderButton = React.forwardRef( }); return ( - diff --git a/src/main/MainMenu.tsx b/src/main/MainMenu.tsx index 1082ad160..1b4f386e3 100644 --- a/src/main/MainMenu.tsx +++ b/src/main/MainMenu.tsx @@ -21,6 +21,7 @@ import { resetPostRequestState } from "../redux/workflowPostSlice"; import { setIsDisplayEditView } from "../redux/subtitleSlice"; import { useTheme } from "../themes"; +import { ProtoButton } from "@opencast/appkit"; import { screenWidthAtMost } from "@opencast/appkit"; /** @@ -154,15 +155,11 @@ export const MainMenuButton: React.FC = ({ }); return ( -
  • ) => { - if (event.key === "Enter") { - onMenuItemClicked(); - } - }} + css={[basicButtonStyle(theme), customCSS ? customCSS : mainMenuButtonStyle]} > = ({ }}> {bottomText} } -
  • +
    ); }; diff --git a/src/main/Save.tsx b/src/main/Save.tsx index 406abedee..49582b51e 100644 --- a/src/main/Save.tsx +++ b/src/main/Save.tsx @@ -33,6 +33,7 @@ import { serializeSubtitle } from "../util/utilityFunctions"; import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; import { Spinner } from "@opencast/appkit"; +import { ProtoButton } from "@opencast/appkit"; import { setEnd } from "../redux/endSlice"; /** @@ -141,18 +142,13 @@ export const SaveButton: React.FC<{ } }; - 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({ @@ -177,18 +173,14 @@ export const SaveButton: React.FC<{ return ( -
    ) => { - if (event.key === " " || event.key === "Enter") { - save(); - } - }}> + css={[basicButtonStyle(theme), navigationButtonStyle(theme)]} + > {Icon()} {text ?? t("save.confirm-button")}
    {ariaSaveUpdate()}
    -
    +
    ); }; diff --git a/src/main/SubtitleEditor.tsx b/src/main/SubtitleEditor.tsx index aa3be72d8..07a957aa3 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"; @@ -21,7 +22,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 @@ -47,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) { @@ -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); @@ -194,13 +239,13 @@ const DownloadButton: React.FC = () => { return ( -
    downloadSubtitles()} + css={[basicButtonStyle(theme), subtitleButtonStyle(theme)]} > {t("subtitles.downloadButton-title")} -
    +
    ); }; @@ -267,13 +312,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..267092862 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 @@ -32,7 +33,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 +44,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 +58,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 +67,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 +113,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; @@ -153,6 +157,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 +184,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}
    -
    +
    ); }; @@ -232,7 +233,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); @@ -270,16 +271,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 578b5973d..26003c3f0 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 8239823da..7be0c65b2 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, css({ padding: "16px", @@ -214,17 +215,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 a66d75f2f..cae09284f 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 @@ -232,19 +233,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 ? : } +
    ); }; @@ -317,17 +314,14 @@ const NextButton: React.FC<{ return ( -
    { - if (event.key === "Enter") { - jumpToNext(); - } - }}> + css={[basicButtonStyle(theme)]} + > -
    +
    ); }; @@ -453,12 +447,15 @@ const VolumeSlider: React.FC<{ return (
    -
    + aria-pressed={isMuted} + aria-hidden={false} + onClick={switchIsMuted} + css={[basicButtonStyle(theme)]} + > {isMuted ? : } -
    +
    (); diff --git a/src/redux/metadataSlice.ts b/src/redux/metadataSlice.ts index fe0424385..26cac6484 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[], @@ -36,7 +37,7 @@ const initialState: metadata & httpRequestState = { errorReason: "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"); } diff --git a/src/redux/subtitleSlice.ts b/src/redux/subtitleSlice.ts index 3fff70fc6..f25269282 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 @@ -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; @@ -250,7 +253,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 +261,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 b7ff287be..abd040880 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/workflowPostSlice.ts b/src/redux/workflowPostSlice.ts index a86291646..c1ec1ec6d 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"); } 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 { 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")'); });