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..75ecc09e9 100644
--- a/src/main/Save.tsx
+++ b/src/main/Save.tsx
@@ -145,18 +145,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({
diff --git a/src/main/SubtitleEditor.tsx b/src/main/SubtitleEditor.tsx
index aa3be72d8..ac3be666c 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";
@@ -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);
diff --git a/src/main/SubtitleSelect.tsx b/src/main/SubtitleSelect.tsx
index c415a8670..173919455 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;
@@ -232,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);
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 {