From a2bed50ff9aca084d23283f8790aa0d895669aa0 Mon Sep 17 00:00:00 2001 From: Dennis Benz Date: Mon, 3 Jul 2023 15:15:35 +0200 Subject: [PATCH 1/8] Add button to delete all segments Ref #1083 --- src/i18n/locales/en-US.json | 3 +++ src/main/CuttingActions.tsx | 7 ++++++- src/redux/videoSlice.ts | 8 +++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index fd8754ccb..3efc78cbd 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -17,6 +17,9 @@ "delete-button": "Delete", "delete-restore-tooltip": "Mark or unmark the segment at the current position as to be deleted. Hotkey: {{hotkeyName}}", "delete-restore-tooltip-aria": "Delete and Restore. Mark or unmark the segment at the current position as to be deleted. Hotkey: {{hotKeyName}}.", + "delete-all-button": "Delete All", + "delete-all-tooltip": "Mark all segments as to be deleted.", + "delete-all-tooltip-aria": "Delete All. Mark all segments as to be deleted.", "restore-button": "Restore", "mergeLeft-button": "Merge Left", "mergeLeft-tooltip": "Combine the currently active segment with the segment to its left. Hotkey: {{hotkeyName}}", diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index 0f3629ff5..03a3b78fc 100644 --- a/src/main/CuttingActions.tsx +++ b/src/main/CuttingActions.tsx @@ -16,7 +16,7 @@ import { css } from '@emotion/react' import { useDispatch, useSelector } from 'react-redux'; import { - cut, markAsDeletedOrAlive, selectIsCurrentSegmentAlive, mergeLeft, mergeRight + cut, markAsDeletedOrAlive, selectIsCurrentSegmentAlive, mergeLeft, mergeRight, markAllAsDeleted } from '../redux/videoSlice' import { GlobalHotKeys, KeySequence, KeyMapOptions } from "react-hotkeys"; import { cuttingKeyMap } from "../globalKeys"; @@ -86,6 +86,11 @@ const CuttingActions: React.FC = () => { + { + state.segments.forEach((segment: Segment) => { + segment.deleted = true; + }); + state.hasChanges = true + }, setSelectedWorkflowIndex: (state, action: PayloadAction) => { state.selectedWorkflowId = action.payload }, @@ -336,7 +342,7 @@ const setThumbnailHelper = (state: video, id: Track["id"], uri: Track["thumbnail export const { setTrackEnabled, setIsPlaying, setIsPlayPreview, setCurrentlyAt, setCurrentlyAtInSeconds, addSegment, setAspectRatio, setHasChanges, setWaveformImages, setThumbnails, setThumbnail, removeThumbnail, - cut, markAsDeletedOrAlive, setSelectedWorkflowIndex, mergeLeft, mergeRight, setPreviewTriggered, + cut, markAsDeletedOrAlive, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, mergeRight, setPreviewTriggered, setClickTriggered } = videoSlice.actions // Export selectors From 03549148f801af2794b3808e7e19854781ed3f62 Mon Sep 17 00:00:00 2001 From: Dennis Benz Date: Tue, 4 Jul 2023 09:16:09 +0200 Subject: [PATCH 2/8] Add delete restore button to each segment of timeline Ref #1083 --- src/i18n/locales/en-US.json | 6 ++++- src/main/Timeline.tsx | 44 ++++++++++++++++++++++++++++++++----- src/redux/videoSlice.ts | 8 +++++-- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 3efc78cbd..a368730bf 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -93,7 +93,11 @@ "generateWaveform-text": "Generating Waveform", "segment-tooltip": "Segment {{segment}}", "scrubber-text-aria": "Timeline marker. {{currentTime}}. Active segment: {{segment}}. {{segmentStatus}}. Controls: {{moveLeft}} and {{moveRight}} to move the timeline marker. {{increase}} and {{decrease}} to increase/decrease the move delta.\n", - "segments-text-aria": "Segment {{index}}. {{segmentStatus}}. Start: {{start}}. End: {{end}}.\n" + "segments-text-aria": "Segment {{index}}. {{segmentStatus}}. Start: {{start}}. End: {{end}}.\n", + "segment-delete-tooltip": "Mark this segment as to be deleted", + "segment-delete-tooltip-aria": "Delete. Mark this segment as to be deleted", + "segment-restore-tooltip": "Unmark this segment as to be deleted", + "segment-restore-tooltip-aria": "Restore. Unmark this segment as to be deleted" }, "workflowConfig": { diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index 6ed81d1c3..408e7ffaf 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from 'react' +import React, { useState, useRef, useEffect, SyntheticEvent } from 'react' import Draggable from 'react-draggable'; @@ -7,11 +7,11 @@ import { css } from '@emotion/react' import { useSelector, useDispatch } from 'react-redux'; import { Segment, httpRequestState } from '../types' import { - selectSegments, selectActiveSegmentIndex, selectDuration, selectVideoURL, selectWaveformImages, setWaveformImages + selectSegments, selectActiveSegmentIndex, selectDuration, selectVideoURL, selectWaveformImages, setWaveformImages, markAsDeletedOrAliveByIndex } from '../redux/videoSlice' import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faBars, faSpinner } from "@fortawesome/free-solid-svg-icons"; +import { faBars, faSpinner, faTrash, faTrashRestore } from "@fortawesome/free-solid-svg-icons"; import useResizeObserver from "use-resize-observer"; @@ -23,8 +23,9 @@ import { scrubberKeyMap } from '../globalKeys'; import { useTranslation } from 'react-i18next'; import { ActionCreatorWithPayload } from '@reduxjs/toolkit'; import { RootState } from '../redux/store'; -import { selectTheme } from '../redux/themeSlice'; +import { selectTheme, Theme } from '../redux/themeSlice'; import { ThemedTooltip } from './Tooltip'; +import { basicButtonStyle } from "../cssStyles"; /** * A container for visualizing the cutting of the video, as well as for controlling @@ -195,7 +196,7 @@ export const Scrubber: React.FC<{ height: timelineHeight - 10 + 'px', // TODO: CHECK IF height: '100%', width: '1px', position: 'absolute', - zIndex: 2, + zIndex: 1, boxShadow: `${theme.boxShadow}`, display: 'flex', flexDirection: 'column', @@ -293,12 +294,21 @@ export const SegmentsList: React.FC<{ }) => { const { t } = useTranslation(); + const theme = useSelector(selectTheme); // Init redux variables + const dispatch = useDispatch() const segments = useSelector(selectSegments) const duration = useSelector(selectDuration) const activeSegmentIndex = useSelector(selectActiveSegmentIndex) + const markAsDeletedOrAlive = (event: KeyboardEvent | SyntheticEvent, index: number) => { + event.preventDefault() // Prevent page scrolling due to Space bar press + event.stopPropagation() // Prevent video playback due to Space bar press + + dispatch(markAsDeletedOrAliveByIndex(index)); + } + /** * Returns a background color based on whether the segment is to be deleted * and whether the segment is currently active @@ -325,6 +335,15 @@ export const SegmentsList: React.FC<{ } } + const markAsDeletedButtonStyle = (theme: Theme) => css({ + position: 'absolute', + right: '8px', + top: '8px', + padding: '5px', + background: `${theme.element_bg}`, + zIndex: 2, + }); + // Render the individual segments const renderedSegments = () => { return ( @@ -338,6 +357,7 @@ export const SegmentsList: React.FC<{ end: convertMsToReadableString(segment.end) })} tabIndex={tabable ? 0 : -1} css={{ + position: 'relative', background: bgColor(segment.deleted, styleByActiveSegment ? activeSegmentIndex === index : false), borderRadius: '5px', borderStyle: styleByActiveSegment ? (activeSegmentIndex === index ? 'dashed' : 'solid') : 'solid', @@ -346,8 +366,20 @@ export const SegmentsList: React.FC<{ boxSizing: 'border-box', width: ((segment.end - segment.start) / duration) * 100 + '%', height: timelineHeight - 20 + 'px', // CHECK IF 100% - zIndex: 1, }}> + +
markAsDeletedOrAlive(event, index)} + onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { + markAsDeletedOrAlive(event, index) + }}} + onMouseDown={(event) => event.stopPropagation() } // Prevent timeline jump + > + +
+
)) diff --git a/src/redux/videoSlice.ts b/src/redux/videoSlice.ts index 797e35842..84f95cc3b 100644 --- a/src/redux/videoSlice.ts +++ b/src/redux/videoSlice.ts @@ -167,6 +167,10 @@ const videoSlice = createSlice({ state.segments[state.activeSegmentIndex].deleted = !state.segments[state.activeSegmentIndex].deleted state.hasChanges = true }, + markAsDeletedOrAliveByIndex: (state, action: PayloadAction) => { + state.segments[action.payload].deleted = !state.segments[action.payload].deleted + state.hasChanges = true + }, markAllAsDeleted: state => { state.segments.forEach((segment: Segment) => { segment.deleted = true; @@ -342,8 +346,8 @@ const setThumbnailHelper = (state: video, id: Track["id"], uri: Track["thumbnail export const { setTrackEnabled, setIsPlaying, setIsPlayPreview, setCurrentlyAt, setCurrentlyAtInSeconds, addSegment, setAspectRatio, setHasChanges, setWaveformImages, setThumbnails, setThumbnail, removeThumbnail, - cut, markAsDeletedOrAlive, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, mergeRight, setPreviewTriggered, - setClickTriggered } = videoSlice.actions + cut, markAsDeletedOrAlive, markAsDeletedOrAliveByIndex, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, + mergeRight, setPreviewTriggered, setClickTriggered } = videoSlice.actions // Export selectors // Selectors mainly pertaining to the video state From 4235cbbf977d37158c25a756e27e5574f84de910 Mon Sep 17 00:00:00 2001 From: Dennis Benz Date: Mon, 3 Jul 2023 15:15:35 +0200 Subject: [PATCH 3/8] Fix code formatting --- src/main/Timeline.tsx | 6 +++--- src/redux/videoSlice.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index 408e7ffaf..a1179e901 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -369,13 +369,13 @@ export const SegmentsList: React.FC<{ }}>
markAsDeletedOrAlive(event, index)} onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { markAsDeletedOrAlive(event, index) - }}} - onMouseDown={(event) => event.stopPropagation() } // Prevent timeline jump + } }} + onMouseDown={event => event.stopPropagation()} // Prevent timeline jump >
diff --git a/src/redux/videoSlice.ts b/src/redux/videoSlice.ts index 84f95cc3b..93119ffb4 100644 --- a/src/redux/videoSlice.ts +++ b/src/redux/videoSlice.ts @@ -346,7 +346,7 @@ const setThumbnailHelper = (state: video, id: Track["id"], uri: Track["thumbnail export const { setTrackEnabled, setIsPlaying, setIsPlayPreview, setCurrentlyAt, setCurrentlyAtInSeconds, addSegment, setAspectRatio, setHasChanges, setWaveformImages, setThumbnails, setThumbnail, removeThumbnail, - cut, markAsDeletedOrAlive, markAsDeletedOrAliveByIndex, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, + cut, markAsDeletedOrAlive, markAsDeletedOrAliveByIndex, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, mergeRight, setPreviewTriggered, setClickTriggered } = videoSlice.actions // Export selectors From 778b8f05cac4a08d91add6d89a82bf425ad7170e Mon Sep 17 00:00:00 2001 From: Dennis Benz Date: Mon, 4 Sep 2023 09:45:33 +0200 Subject: [PATCH 4/8] Revert "Add delete restore button to each segment of timeline" This reverts commit 03549148 --- src/i18n/locales/en-US.json | 6 +---- src/main/Timeline.tsx | 44 +++++-------------------------------- src/redux/videoSlice.ts | 8 ++----- 3 files changed, 9 insertions(+), 49 deletions(-) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index a368730bf..3efc78cbd 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -93,11 +93,7 @@ "generateWaveform-text": "Generating Waveform", "segment-tooltip": "Segment {{segment}}", "scrubber-text-aria": "Timeline marker. {{currentTime}}. Active segment: {{segment}}. {{segmentStatus}}. Controls: {{moveLeft}} and {{moveRight}} to move the timeline marker. {{increase}} and {{decrease}} to increase/decrease the move delta.\n", - "segments-text-aria": "Segment {{index}}. {{segmentStatus}}. Start: {{start}}. End: {{end}}.\n", - "segment-delete-tooltip": "Mark this segment as to be deleted", - "segment-delete-tooltip-aria": "Delete. Mark this segment as to be deleted", - "segment-restore-tooltip": "Unmark this segment as to be deleted", - "segment-restore-tooltip-aria": "Restore. Unmark this segment as to be deleted" + "segments-text-aria": "Segment {{index}}. {{segmentStatus}}. Start: {{start}}. End: {{end}}.\n" }, "workflowConfig": { diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index a1179e901..6ed81d1c3 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect, SyntheticEvent } from 'react' +import React, { useState, useRef, useEffect } from 'react' import Draggable from 'react-draggable'; @@ -7,11 +7,11 @@ import { css } from '@emotion/react' import { useSelector, useDispatch } from 'react-redux'; import { Segment, httpRequestState } from '../types' import { - selectSegments, selectActiveSegmentIndex, selectDuration, selectVideoURL, selectWaveformImages, setWaveformImages, markAsDeletedOrAliveByIndex + selectSegments, selectActiveSegmentIndex, selectDuration, selectVideoURL, selectWaveformImages, setWaveformImages } from '../redux/videoSlice' import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faBars, faSpinner, faTrash, faTrashRestore } from "@fortawesome/free-solid-svg-icons"; +import { faBars, faSpinner } from "@fortawesome/free-solid-svg-icons"; import useResizeObserver from "use-resize-observer"; @@ -23,9 +23,8 @@ import { scrubberKeyMap } from '../globalKeys'; import { useTranslation } from 'react-i18next'; import { ActionCreatorWithPayload } from '@reduxjs/toolkit'; import { RootState } from '../redux/store'; -import { selectTheme, Theme } from '../redux/themeSlice'; +import { selectTheme } from '../redux/themeSlice'; import { ThemedTooltip } from './Tooltip'; -import { basicButtonStyle } from "../cssStyles"; /** * A container for visualizing the cutting of the video, as well as for controlling @@ -196,7 +195,7 @@ export const Scrubber: React.FC<{ height: timelineHeight - 10 + 'px', // TODO: CHECK IF height: '100%', width: '1px', position: 'absolute', - zIndex: 1, + zIndex: 2, boxShadow: `${theme.boxShadow}`, display: 'flex', flexDirection: 'column', @@ -294,21 +293,12 @@ export const SegmentsList: React.FC<{ }) => { const { t } = useTranslation(); - const theme = useSelector(selectTheme); // Init redux variables - const dispatch = useDispatch() const segments = useSelector(selectSegments) const duration = useSelector(selectDuration) const activeSegmentIndex = useSelector(selectActiveSegmentIndex) - const markAsDeletedOrAlive = (event: KeyboardEvent | SyntheticEvent, index: number) => { - event.preventDefault() // Prevent page scrolling due to Space bar press - event.stopPropagation() // Prevent video playback due to Space bar press - - dispatch(markAsDeletedOrAliveByIndex(index)); - } - /** * Returns a background color based on whether the segment is to be deleted * and whether the segment is currently active @@ -335,15 +325,6 @@ export const SegmentsList: React.FC<{ } } - const markAsDeletedButtonStyle = (theme: Theme) => css({ - position: 'absolute', - right: '8px', - top: '8px', - padding: '5px', - background: `${theme.element_bg}`, - zIndex: 2, - }); - // Render the individual segments const renderedSegments = () => { return ( @@ -357,7 +338,6 @@ export const SegmentsList: React.FC<{ end: convertMsToReadableString(segment.end) })} tabIndex={tabable ? 0 : -1} css={{ - position: 'relative', background: bgColor(segment.deleted, styleByActiveSegment ? activeSegmentIndex === index : false), borderRadius: '5px', borderStyle: styleByActiveSegment ? (activeSegmentIndex === index ? 'dashed' : 'solid') : 'solid', @@ -366,20 +346,8 @@ export const SegmentsList: React.FC<{ boxSizing: 'border-box', width: ((segment.end - segment.start) / duration) * 100 + '%', height: timelineHeight - 20 + 'px', // CHECK IF 100% + zIndex: 1, }}> - -
markAsDeletedOrAlive(event, index)} - onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { - markAsDeletedOrAlive(event, index) - } }} - onMouseDown={event => event.stopPropagation()} // Prevent timeline jump - > - -
-
)) diff --git a/src/redux/videoSlice.ts b/src/redux/videoSlice.ts index 93119ffb4..797e35842 100644 --- a/src/redux/videoSlice.ts +++ b/src/redux/videoSlice.ts @@ -167,10 +167,6 @@ const videoSlice = createSlice({ state.segments[state.activeSegmentIndex].deleted = !state.segments[state.activeSegmentIndex].deleted state.hasChanges = true }, - markAsDeletedOrAliveByIndex: (state, action: PayloadAction) => { - state.segments[action.payload].deleted = !state.segments[action.payload].deleted - state.hasChanges = true - }, markAllAsDeleted: state => { state.segments.forEach((segment: Segment) => { segment.deleted = true; @@ -346,8 +342,8 @@ const setThumbnailHelper = (state: video, id: Track["id"], uri: Track["thumbnail export const { setTrackEnabled, setIsPlaying, setIsPlayPreview, setCurrentlyAt, setCurrentlyAtInSeconds, addSegment, setAspectRatio, setHasChanges, setWaveformImages, setThumbnails, setThumbnail, removeThumbnail, - cut, markAsDeletedOrAlive, markAsDeletedOrAliveByIndex, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, - mergeRight, setPreviewTriggered, setClickTriggered } = videoSlice.actions + cut, markAsDeletedOrAlive, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, mergeRight, setPreviewTriggered, + setClickTriggered } = videoSlice.actions // Export selectors // Selectors mainly pertaining to the video state From 212ab47197bd28905a5db6640272a6c8017c0897 Mon Sep 17 00:00:00 2001 From: Dennis Benz Date: Mon, 4 Sep 2023 12:45:01 +0200 Subject: [PATCH 5/8] Change delete all button to merge all segments button Ref #1083 --- src/i18n/locales/en-US.json | 6 +++--- src/main/CuttingActions.tsx | 13 +++++++------ src/redux/videoSlice.ts | 35 +++++++++++++++++++---------------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 3efc78cbd..dd1fe8330 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -17,9 +17,9 @@ "delete-button": "Delete", "delete-restore-tooltip": "Mark or unmark the segment at the current position as to be deleted. Hotkey: {{hotkeyName}}", "delete-restore-tooltip-aria": "Delete and Restore. Mark or unmark the segment at the current position as to be deleted. Hotkey: {{hotKeyName}}.", - "delete-all-button": "Delete All", - "delete-all-tooltip": "Mark all segments as to be deleted.", - "delete-all-tooltip-aria": "Delete All. Mark all segments as to be deleted.", + "merge-all-button": "Merge All", + "merge-all-tooltip": "Combine all segments into a single segment.", + "merge-all-tooltip-aria": "Merge All. Combine all segments into a single segment.", "restore-button": "Restore", "mergeLeft-button": "Merge Left", "mergeLeft-tooltip": "Combine the currently active segment with the segment to its left. Hotkey: {{hotkeyName}}", diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index 03a3b78fc..1d3c3bef3 100644 --- a/src/main/CuttingActions.tsx +++ b/src/main/CuttingActions.tsx @@ -5,6 +5,7 @@ import { basicButtonStyle, flexGapReplacementStyle } from '../cssStyles' import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { + faArrowsLeftRightToLine, faCut, faStepBackward, faStepForward, @@ -16,7 +17,7 @@ import { css } from '@emotion/react' import { useDispatch, useSelector } from 'react-redux'; import { - cut, markAsDeletedOrAlive, selectIsCurrentSegmentAlive, mergeLeft, mergeRight, markAllAsDeleted + cut, markAsDeletedOrAlive, selectIsCurrentSegmentAlive, mergeLeft, mergeRight, mergeAll } from '../redux/videoSlice' import { GlobalHotKeys, KeySequence, KeyMapOptions } from "react-hotkeys"; import { cuttingKeyMap } from "../globalKeys"; @@ -86,11 +87,6 @@ const CuttingActions: React.FC = () => { - { tooltip={t('cuttingActions.mergeRight-tooltip', { hotkeyName: (cuttingKeyMap[handlers.mergeRight.name] as KeyMapOptions).sequence })} ariaLabelText={t('cuttingActions.mergeRight-tooltip-aria', { hotkeyName: (cuttingKeyMap[handlers.mergeRight.name] as KeyMapOptions).sequence })} /> +
{/* { - state.segments.forEach((segment: Segment) => { - segment.deleted = true; - }); - state.hasChanges = true - }, setSelectedWorkflowIndex: (state, action: PayloadAction) => { state.selectedWorkflowId = action.payload }, @@ -184,6 +178,10 @@ const videoSlice = createSlice({ mergeSegments(state, state.activeSegmentIndex, state.activeSegmentIndex + 1) state.hasChanges = true }, + mergeAll: state => { + mergeSegments(state, 0, state.segments.length - 1) + state.hasChanges = true + }, }, // For Async Requests extraReducers: builder => { @@ -264,22 +262,27 @@ export const parseSegments = (segments: Segment[], duration: number) => { } /** - * Helper function for merging two segments + * Helper function for merging segments */ -const mergeSegments = (state: video, activeSegmentIndex: number, mergeSegmentIndex: number) => { +const mergeSegments = (state: video, startSegmentIndex: number, endSegmentIndex: number) => { // Check if mergeSegmentIndex is valid - if (mergeSegmentIndex < 0 || mergeSegmentIndex > state.segments.length - 1) { + if (endSegmentIndex < 0 || endSegmentIndex > state.segments.length - 1) { return } + const minSegmentIndex = Math.min(startSegmentIndex, endSegmentIndex) + // Increase activeSegment length - state.segments[activeSegmentIndex].start = Math.min( - state.segments[activeSegmentIndex].start, state.segments[mergeSegmentIndex].start) - state.segments[activeSegmentIndex].end = Math.max( - state.segments[activeSegmentIndex].end, state.segments[mergeSegmentIndex].end) + state.segments[minSegmentIndex].start = Math.min( + state.segments[startSegmentIndex].start, state.segments[endSegmentIndex].start) + state.segments[minSegmentIndex].end = Math.max( + state.segments[startSegmentIndex].end, state.segments[endSegmentIndex].end) - // Remove the other segment - state.segments.splice(mergeSegmentIndex, 1); + // Remove the last segment and segments between + state.segments.splice( + minSegmentIndex + 1, + Math.abs(endSegmentIndex - startSegmentIndex) + ); // Update active segment updateActiveSegment(state) @@ -342,7 +345,7 @@ const setThumbnailHelper = (state: video, id: Track["id"], uri: Track["thumbnail export const { setTrackEnabled, setIsPlaying, setIsPlayPreview, setCurrentlyAt, setCurrentlyAtInSeconds, addSegment, setAspectRatio, setHasChanges, setWaveformImages, setThumbnails, setThumbnail, removeThumbnail, - cut, markAsDeletedOrAlive, markAllAsDeleted, setSelectedWorkflowIndex, mergeLeft, mergeRight, setPreviewTriggered, + cut, markAsDeletedOrAlive, setSelectedWorkflowIndex, mergeLeft, mergeRight, mergeAll, setPreviewTriggered, setClickTriggered } = videoSlice.actions // Export selectors From f34f771271534fa141e52e6828374cef475de20d Mon Sep 17 00:00:00 2001 From: Dennis Benz Date: Mon, 4 Sep 2023 15:47:30 +0200 Subject: [PATCH 6/8] Fix incorrect state inheritance during merges --- src/redux/videoSlice.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/redux/videoSlice.ts b/src/redux/videoSlice.ts index 50594e375..36df9d0a5 100644 --- a/src/redux/videoSlice.ts +++ b/src/redux/videoSlice.ts @@ -270,17 +270,15 @@ const mergeSegments = (state: video, startSegmentIndex: number, endSegmentIndex: return } - const minSegmentIndex = Math.min(startSegmentIndex, endSegmentIndex) - // Increase activeSegment length - state.segments[minSegmentIndex].start = Math.min( + state.segments[startSegmentIndex].start = Math.min( state.segments[startSegmentIndex].start, state.segments[endSegmentIndex].start) - state.segments[minSegmentIndex].end = Math.max( + state.segments[startSegmentIndex].end = Math.max( state.segments[startSegmentIndex].end, state.segments[endSegmentIndex].end) - // Remove the last segment and segments between + // Remove the end segment and segments between state.segments.splice( - minSegmentIndex + 1, + startSegmentIndex < endSegmentIndex ? startSegmentIndex + 1 : endSegmentIndex, Math.abs(endSegmentIndex - startSegmentIndex) ); From 9f1d6241f09094bbc8eb6d787cf8e74bba6f6cbf Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 18 Sep 2023 05:06:39 +0000 Subject: [PATCH 7/8] Automatically update translation keys --- src/i18n/locales/cs-CZ.json | 24 ++++++++++++++---------- src/i18n/locales/de-DE.json | 24 ++++++++++++++---------- src/i18n/locales/el-GR.json | 24 ++++++++++++++---------- src/i18n/locales/es-ES.json | 24 ++++++++++++++---------- src/i18n/locales/fr-FR.json | 24 ++++++++++++++---------- src/i18n/locales/nl-NL.json | 24 ++++++++++++++---------- src/i18n/locales/zh-CN.json | 24 ++++++++++++++---------- src/i18n/locales/zh-TW.json | 24 ++++++++++++++---------- 8 files changed, 112 insertions(+), 80 deletions(-) diff --git a/src/i18n/locales/cs-CZ.json b/src/i18n/locales/cs-CZ.json index d1ba92324..9e9e3eb2e 100644 --- a/src/i18n/locales/cs-CZ.json +++ b/src/i18n/locales/cs-CZ.json @@ -54,7 +54,9 @@ "info-text": "The video will not be processed but all cutting information will be stored in Opencast. You can continue your edit later.", "success-text": "Changes saved successfully! You can now close the editor or continue working.", "success-tooltip-aria": "Saved successfully", - "saveArea-tooltip": "Save Area" + "saveArea-tooltip": "Save Area", + "confirm-success": "Okay", + "cancel-save": "Don't save" }, "discard": { "headline-text": "Discard changes", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Slovenian", - "LANGUAGES-PORTUGUESE": "Portuguese", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "Romansh", "LANGUAGES-ARABIC": "Arabic", "LANGUAGES-POLISH": "Polish", @@ -202,7 +204,7 @@ "goBack-button": "No, take me back" }, "trackSelection": { - "description": "Select or deselect which tracks are used for processing and publication.", + "title": "Select track(s) for processing", "trackInactive": "inactive", "deleteTrackText": "Delete Track", "restoreTrackText": "Restore Track", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Timeline overview" }, "keyboardControls": { - "header": "Keyboard Controls", + "header": "Shortcuts", "defaultGroupName": "General", "missingLabel": "Unknown", "groupVideoPlayer": "Video Player", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Appearance", - "selectThemesLabel": "Appearances", - "darkmode": "Dark mode", - "lightmode": "Light mode", - "system": "System design", - "high-contrast-dark": "High contrast (Dark mode)", - "high-contrast-light": "High contrast (Light mode)" + "dark": "Dark", + "light": "Light", + "auto": "Auto", + "dark-high-contrast": "Dark (High contrast)", + "light-high-contrast": "Light (High contrast)" + }, + "language": { + "language": "Language" } } diff --git a/src/i18n/locales/de-DE.json b/src/i18n/locales/de-DE.json index e5be67f4a..d43d94192 100644 --- a/src/i18n/locales/de-DE.json +++ b/src/i18n/locales/de-DE.json @@ -54,7 +54,9 @@ "info-text": "Das Video wird nicht verarbeitet, aber alle Schnittinformationen werden in Opencast gespeichert. Sie können Ihre Bearbeitung später fortsetzen.", "success-text": "Änderungen erfolgreich gespeichert! Sie können den Editor nun schließen oder weiter arbeiten.", "success-tooltip-aria": "Erfolgreich gespeichert", - "saveArea-tooltip": "Speicherbereich" + "saveArea-tooltip": "Speicherbereich", + "confirm-success": "Okay", + "cancel-save": "Nicht speichern" }, "discard": { "headline-text": "Änderungen verwerfen", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Slowenisch", - "LANGUAGES-PORTUGUESE": "Portugiesisch", + "LANGUAGES-PORTUGESE": "Portugiesisch", "LANGUAGES-ROMANSH": "Rätoromanisch", "LANGUAGES-ARABIC": "Arabisch", "LANGUAGES-POLISH": "Polnisch", @@ -202,7 +204,7 @@ "goBack-button": "Nein, zurück" }, "trackSelection": { - "description": "Wählen oder deaktivieren Sie die Spuren für die Verarbeitung und Veröffentlichung.", + "title": "Spur(en) für die Verarbeitung auswählen", "trackInactive": "inaktiv", "deleteTrackText": "Spur löschen", "restoreTrackText": "Spur wiederherstellen", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Zeitleiste-Übersicht" }, "keyboardControls": { - "header": "Tastatursteuerung", + "header": "Tastenkürzel", "defaultGroupName": "Allgemein", "missingLabel": "Unbekannt", "groupVideoPlayer": "Videoplayer", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Erscheinungsbild", - "selectThemesLabel": "Darstellung", - "darkmode": "Dunkles Design", - "lightmode": "Helles Design", - "system": "System Design", - "high-contrast-dark": "Hoher Kontrast (Dunkles Design)", - "high-contrast-light": "Hoher Kontrast (Helles Design)" + "dark": "Dunkel", + "light": "Hell", + "auto": "Auto", + "dark-high-contrast": "Dunkel (hoher Kontrast)", + "light-high-contrast": "Hell (hoher Kontrast)" + }, + "language": { + "language": "Sprache" } } diff --git a/src/i18n/locales/el-GR.json b/src/i18n/locales/el-GR.json index b34b7658a..80dbcb062 100644 --- a/src/i18n/locales/el-GR.json +++ b/src/i18n/locales/el-GR.json @@ -54,7 +54,9 @@ "info-text": "Το βίντεο δεν θα επεξεργαστεί, αλλά όλες οι πληροφορίες κοπής θα αποθηκευτούν στο Opencast. Μπορείτε να συνεχίσετε την επεξεργασία σας αργότερα.", "success-text": "Οι αλλαγές αποθηκεύτηκαν επιτυχώς! Τώρα μπορείτε να κλείσετε τον επεξεργαστή βίντεο ή να συνεχίσετε να εργάζεστε στο βίντεο.", "success-tooltip-aria": "Αποθηκεύτηκε επιτυχώς", - "saveArea-tooltip": "Αποθήκευση Περιοχής" + "saveArea-tooltip": "Αποθήκευση Περιοχής", + "confirm-success": "Okay", + "cancel-save": "Don't save" }, "discard": { "headline-text": "Απόρριψη αλλαγών", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Σλοβενικά", - "LANGUAGES-PORTUGUESE": "Πορτογαλικά", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "Ρωμανικά", "LANGUAGES-ARABIC": "Αραβικά", "LANGUAGES-POLISH": "Πολωνικά", @@ -202,7 +204,7 @@ "goBack-button": "Όχι, πήγαινέ με πίσω" }, "trackSelection": { - "description": "Επιλέξτε ή αποεπιλέξτε ποια κομμάτια χρησιμοποιούνται για την επεξεργασία και τη δημοσίευση.", + "title": "Select track(s) for processing", "trackInactive": "ανενεργό", "deleteTrackText": "Διαγραφή Κομματιού", "restoreTrackText": "Επαναφορά Κομματιού", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Επισκόπηση χρονολογίου" }, "keyboardControls": { - "header": "Έλεγχοι Πληκτρολογίου", + "header": "Shortcuts", "defaultGroupName": "Γενικά", "missingLabel": "Άγνωστο", "groupVideoPlayer": "Αναπαραγωγή Βίντεο", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Εμφάνιση", - "selectThemesLabel": "Εμφανίσεις", - "darkmode": "Σκοτεινή λειτουργία", - "lightmode": "Φωτεινή λειτουργία", - "system": "Σχεδιασμός συστήματος", - "high-contrast-dark": "Υψηλή αντίθεση (Λειτουργία μαύρου)", - "high-contrast-light": "Υψηλή αντίθεση (λειτουργία άσπρου)" + "dark": "Dark", + "light": "Light", + "auto": "Auto", + "dark-high-contrast": "Dark (High contrast)", + "light-high-contrast": "Light (High contrast)" + }, + "language": { + "language": "Γλώσσα" } } diff --git a/src/i18n/locales/es-ES.json b/src/i18n/locales/es-ES.json index b8000820e..207041843 100644 --- a/src/i18n/locales/es-ES.json +++ b/src/i18n/locales/es-ES.json @@ -54,7 +54,9 @@ "info-text": "El vídeo no será procesado, pero toda la información de corte será almacenada en Opencast. Puede continuar su edición más tarde.", "success-text": "¡Cambios guardados con éxito! Ahora puede cerrar el editor o continuar trabajando.", "success-tooltip-aria": "Guardado con éxito", - "saveArea-tooltip": "Guardar área" + "saveArea-tooltip": "Guardar área", + "confirm-success": "Okay", + "cancel-save": "Don't save" }, "discard": { "headline-text": "Descartar cambios", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Esloveno", - "LANGUAGES-PORTUGUESE": "Portugués", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "Rumano", "LANGUAGES-ARABIC": "Árabe", "LANGUAGES-POLISH": "Polaco", @@ -202,7 +204,7 @@ "goBack-button": "¡De ninguna manera! Volver atrás" }, "trackSelection": { - "description": "Seleccione qué pistas serán usadas para el procesamiento y publicación.", + "title": "Select track(s) for processing", "trackInactive": "inactivo", "deleteTrackText": "Eliminar pista", "restoreTrackText": "Restaurar pista", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Previsualización de la línea de tiempo" }, "keyboardControls": { - "header": "Controles del teclado", + "header": "Shortcuts", "defaultGroupName": "General", "missingLabel": "Desconocido", "groupVideoPlayer": "Reproductor de vídeo", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Apariencia", - "selectThemesLabel": "Apariencias", - "darkmode": "Modo oscuro", - "lightmode": "Modo claro", - "system": "Diseño del sistema", - "high-contrast-dark": "Alto contraste (modo oscuro)", - "high-contrast-light": "Alto contraste (modo claro)" + "dark": "Dark", + "light": "Light", + "auto": "Auto", + "dark-high-contrast": "Dark (High contrast)", + "light-high-contrast": "Light (High contrast)" + }, + "language": { + "language": "Idioma" } } diff --git a/src/i18n/locales/fr-FR.json b/src/i18n/locales/fr-FR.json index eaf7789c3..cdc363eb3 100644 --- a/src/i18n/locales/fr-FR.json +++ b/src/i18n/locales/fr-FR.json @@ -54,7 +54,9 @@ "info-text": "La vidéo ne sera pas traitée mais toutes les informations de coupe seront stockées dans Opencast. Vous pouvez continuer votre modification plus tard.", "success-text": "Changes saved successfully! You can now close the editor or continue working.", "success-tooltip-aria": "Sauvegarde réussie", - "saveArea-tooltip": "Enregistrer la zone" + "saveArea-tooltip": "Enregistrer la zone", + "confirm-success": "Okay", + "cancel-save": "Don't save" }, "discard": { "headline-text": "Ignorer les modifications", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Slovaque", - "LANGUAGES-PORTUGUESE": "Portuguese", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "Romanche", "LANGUAGES-ARABIC": "Arabe", "LANGUAGES-POLISH": "Polonais", @@ -202,7 +204,7 @@ "goBack-button": "Non, laissez-moi revenir en arrière." }, "trackSelection": { - "description": "Sélectionnez ou désélectionnez quelles pistes sont utilisées pour le traitement et la publication.", + "title": "Select track(s) for processing", "trackInactive": "inactif", "deleteTrackText": "Supprimer la piste", "restoreTrackText": "Restaurer la piste", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Timeline overview" }, "keyboardControls": { - "header": "Controles clavier", + "header": "Shortcuts", "defaultGroupName": "Général", "missingLabel": "Inconnu", "groupVideoPlayer": "Video Player", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Appearance", - "selectThemesLabel": "Appearances", - "darkmode": "Dark mode", - "lightmode": "Light mode", - "system": "System design", - "high-contrast-dark": "High contrast (Dark mode)", - "high-contrast-light": "High contrast (Light mode)" + "dark": "Dark", + "light": "Light", + "auto": "Auto", + "dark-high-contrast": "Dark (High contrast)", + "light-high-contrast": "Light (High contrast)" + }, + "language": { + "language": "Langue" } } diff --git a/src/i18n/locales/nl-NL.json b/src/i18n/locales/nl-NL.json index d57dcb9c2..92531b53e 100644 --- a/src/i18n/locales/nl-NL.json +++ b/src/i18n/locales/nl-NL.json @@ -54,7 +54,9 @@ "info-text": "De video wordt niet verwerkt, maar alle snijgegevens worden opgeslagen in Opencast. U kunt later doorgaan met uw bewerking.", "success-text": "Changes saved successfully! You can now close the editor or continue working.", "success-tooltip-aria": "Succesvol opgeslagen", - "saveArea-tooltip": "Gebied opslaan" + "saveArea-tooltip": "Gebied opslaan", + "confirm-success": "Okay", + "cancel-save": "Don't save" }, "discard": { "headline-text": "Annuleren", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Sloveens", - "LANGUAGES-PORTUGUESE": "Portuguese", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "Reto-Romaans", "LANGUAGES-ARABIC": "Arabisch", "LANGUAGES-POLISH": "Pools", @@ -202,7 +204,7 @@ "goBack-button": "Nee, breng me terug" }, "trackSelection": { - "description": "Selecteer of deselecteer welke sporen worden gebruikt voor verwerking en publicatie.", + "title": "Select track(s) for processing", "trackInactive": "inactief", "deleteTrackText": "Verwijder spoor", "restoreTrackText": "Herstel spoor", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Timeline overview" }, "keyboardControls": { - "header": "Keyboard Controls", + "header": "Shortcuts", "defaultGroupName": "General", "missingLabel": "Unknown", "groupVideoPlayer": "Video Player", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Appearance", - "selectThemesLabel": "Appearances", - "darkmode": "Dark mode", - "lightmode": "Light mode", - "system": "System design", - "high-contrast-dark": "High contrast (Dark mode)", - "high-contrast-light": "High contrast (Light mode)" + "dark": "Dark", + "light": "Light", + "auto": "Auto", + "dark-high-contrast": "Dark (High contrast)", + "light-high-contrast": "Light (High contrast)" + }, + "language": { + "language": "Taal" } } diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 63c370773..3848ea9d9 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -54,7 +54,9 @@ "info-text": "The video will not be processed but all cutting information will be stored in Opencast. You can continue your edit later.", "success-text": "Changes saved successfully! You can now close the editor or continue working.", "success-tooltip-aria": "Saved successfully", - "saveArea-tooltip": "Save Area" + "saveArea-tooltip": "Save Area", + "confirm-success": "Okay", + "cancel-save": "Don't save" }, "discard": { "headline-text": "Discard changes", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Slovenian", - "LANGUAGES-PORTUGUESE": "Portuguese", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "Romansh", "LANGUAGES-ARABIC": "Arabic", "LANGUAGES-POLISH": "Polish", @@ -202,7 +204,7 @@ "goBack-button": "No, take me back" }, "trackSelection": { - "description": "Select or deselect which tracks are used for processing and publication.", + "title": "Select track(s) for processing", "trackInactive": "inactive", "deleteTrackText": "Delete Track", "restoreTrackText": "Restore Track", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Timeline overview" }, "keyboardControls": { - "header": "键盘设置", + "header": "Shortcuts", "defaultGroupName": "General", "missingLabel": "Unknown", "groupVideoPlayer": "Video Player", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Appearance", - "selectThemesLabel": "Appearances", - "darkmode": "Dark mode", - "lightmode": "Light mode", - "system": "System design", - "high-contrast-dark": "High contrast (Dark mode)", - "high-contrast-light": "High contrast (Light mode)" + "dark": "Dark", + "light": "Light", + "auto": "Auto", + "dark-high-contrast": "Dark (High contrast)", + "light-high-contrast": "Light (High contrast)" + }, + "language": { + "language": "Language" } } diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index f25181c72..c4109c259 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -54,7 +54,9 @@ "info-text": "影片不會被處理,但所有裁剪訊息都將存儲在 Opencast 中。 您可以稍後繼續編輯。", "success-text": "Changes saved successfully! You can now close the editor or continue working.", "success-tooltip-aria": "儲存成功", - "saveArea-tooltip": "儲存區域" + "saveArea-tooltip": "儲存區域", + "confirm-success": "Okay", + "cancel-save": "Don't save" }, "discard": { "headline-text": "放棄變更", @@ -121,7 +123,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "斯洛文尼亞語", - "LANGUAGES-PORTUGUESE": "Portuguese", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "羅曼什語", "LANGUAGES-ARABIC": "阿拉伯語", "LANGUAGES-POLISH": "波蘭語", @@ -202,7 +204,7 @@ "goBack-button": "不,回到上一步" }, "trackSelection": { - "description": "選擇或取消選擇用於處理和發佈的影音軌道。", + "title": "Select track(s) for processing", "trackInactive": "不活動的", "deleteTrackText": "刪除影音軌道", "restoreTrackText": "還原影音軌道", @@ -244,7 +246,7 @@ "overviewTimelineTooltip": "Timeline overview" }, "keyboardControls": { - "header": "鍵盤控制", + "header": "Shortcuts", "defaultGroupName": "一般", "missingLabel": "未知", "groupVideoPlayer": "Video Player", @@ -261,11 +263,13 @@ }, "theme": { "appearance": "Appearance", - "selectThemesLabel": "Appearances", - "darkmode": "Dark mode", - "lightmode": "Light mode", - "system": "System design", - "high-contrast-dark": "High contrast (Dark mode)", - "high-contrast-light": "High contrast (Light mode)" + "dark": "Dark", + "light": "Light", + "auto": "Auto", + "dark-high-contrast": "Dark (High contrast)", + "light-high-contrast": "Light (High contrast)" + }, + "language": { + "language": "語言" } } From e0eccd2609a5e0bfc3d9c8967c1b37707f42a020 Mon Sep 17 00:00:00 2001 From: Dennis Benz Date: Thu, 21 Sep 2023 11:50:04 +0200 Subject: [PATCH 8/8] Fix still incorrect state inheritance during merges --- src/redux/videoSlice.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/redux/videoSlice.ts b/src/redux/videoSlice.ts index f5a5aa5da..7512131d7 100644 --- a/src/redux/videoSlice.ts +++ b/src/redux/videoSlice.ts @@ -179,7 +179,8 @@ const videoSlice = createSlice({ state.hasChanges = true }, mergeAll: state => { - mergeSegments(state, 0, state.segments.length - 1) + mergeSegments(state, state.activeSegmentIndex, 0) + mergeSegments(state, state.activeSegmentIndex, state.segments.length - 1) state.hasChanges = true }, },