From 823cb9b75e0900631adccd7baabefaa982c4d8f2 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 9 Jun 2023 15:12:39 +0200 Subject: [PATCH 001/107] Redesign - Header Style Styles header according to redesgin --- src/main/Header.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/Header.tsx b/src/main/Header.tsx index 95d8bcc4b..6debc6fc2 100644 --- a/src/main/Header.tsx +++ b/src/main/Header.tsx @@ -7,23 +7,30 @@ import { css } from '@emotion/react' function Header() { const themeState = useSelector(selectThemeState); - const header = themeState.startsWith('high-contrast-') + const headerStyle = css({ + display: 'flex', + alignItems: 'center', + }) + + const headerStyleThemed = themeState.startsWith('high-contrast-') ? css({ - height: '46px', + height: '58px', backgroundColor: '#000', borderBottom: '2px solid white' }) : css({ - height: '48px', - backgroundColor: '#333333', + + height: '60px', + backgroundColor: '#4b5563', }) const logo = css({ height: '48px', + marginLeft: '6px', }) return ( -
+
Opencast Editor
); From caecebc04920cd4e4714f1e639042b92bd156ec0 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 9 Jun 2023 16:15:29 +0200 Subject: [PATCH 002/107] Redesign - Main Menu Text Changes the main menu text to blue and bold. Tries to keep text black where suggested. --- src/cssStyles.tsx | 9 ++++++--- src/main/Metadata.tsx | 8 +++++--- src/main/Thumbnail.tsx | 3 ++- src/main/Video.tsx | 23 +++++++++++++++++++---- src/redux/themeSlice.ts | 1 + src/themes.ts | 10 +++++++--- 6 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index ab765641b..5e0c75d38 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -95,7 +95,8 @@ export const basicButtonStyle = (theme: Theme) => css({ alignItems: 'center', ...(flexGapReplacementStyle(10, false)), textAlign: 'center' as const, - outline: `${theme.button_outline}` + outline: `${theme.button_outline}`, + fontWeight: 'bold', }); /** @@ -167,23 +168,25 @@ export const disableButtonAnimation = css({ /** * CSS for a title */ -export const titleStyle = css(({ +export const titleStyle = (theme: Theme) => css(({ display: 'inline-block', padding: '15px', overflow: 'hidden', whiteSpace: "nowrap", textOverflow: 'ellipsis', maxWidth: '100%', + color: `${theme.text_black}`, })) /** * Addendum for the titleStyle * Used for page titles */ -export const titleStyleBold = css({ +export const titleStyleBold = (theme: Theme) => css({ fontWeight: 'bold', fontSize: '24px', verticalAlign: '-2.5px', + color: `${theme.text_black}`, }) /** diff --git a/src/main/Metadata.tsx b/src/main/Metadata.tsx index 4bc69bc75..11fda4bc1 100644 --- a/src/main/Metadata.tsx +++ b/src/main/Metadata.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import { css } from '@emotion/react' -import { calendarStyle, errorBoxStyle, selectFieldStyle } from '../cssStyles' +import { calendarStyle, errorBoxStyle, selectFieldStyle, titleStyle, titleStyleBold } from '../cssStyles' import { useSelector, useDispatch } from 'react-redux'; import { @@ -642,13 +642,15 @@ const Metadata: React.FC<{}> = () => { catalogIndex: number, configureFields: { [key: string]: configureFieldsAttributes } ) => { + + return (
-

+
{i18n.exists(`metadata.${catalog.title.replaceAll(".", "-")}`) ? t(`metadata.${catalog.title.replaceAll(".", "-")}` as TFuncKey) : catalog.title } -

+
{catalog.fields.map((field, i) => { // Render fields based on given array (usually parsed from config settings) diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index d5f9f9d69..6915c5205 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -26,6 +26,7 @@ const Thumbnail : React.FC<{}> = () => { const { t } = useTranslation() const dispatch = useDispatch() + const theme = useSelector(selectTheme); const originalThumbnails = useSelector(selectOriginalThumbnails) // Generate Refs @@ -89,7 +90,7 @@ const Thumbnail : React.FC<{}> = () => { return (
-
{t('thumbnail.title')}
+
{t('thumbnail.title')}
css({ + display: 'inline-block', + flexWrap: 'nowrap', + color: `${theme.text_black}` + }) + return ( -
+
{t("video.previewButton")}
@@ -648,18 +654,26 @@ const TimeDisplay: React.FC<{ // Init redux variables const currentlyAt = useSelector(selectCurrentlyAt) const duration = useSelector(selectDuration) + const theme = useSelector(selectTheme) + + const timeTextStyle = (theme: Theme) => css({ + display: 'inline-block', + width: '100px', + color: `${theme.text_black}` + }) return (
- {" / "} -
+
{new Date((duration ? duration : 0)).toISOString().substr(11, 12)}
@@ -674,9 +688,10 @@ const VideoHeader: React.FC<{}> = () => { const title = useSelector(selectTitle) const metadataTitle = useSelector(selectTitleFromEpisodeDc) + const theme = useSelector(selectTheme); return ( -
+
{metadataTitle ? metadataTitle : title}
); diff --git a/src/redux/themeSlice.ts b/src/redux/themeSlice.ts index 3e2664bb4..89831496e 100644 --- a/src/redux/themeSlice.ts +++ b/src/redux/themeSlice.ts @@ -5,6 +5,7 @@ export interface Theme { background: String menu_background: String text: String + text_black: String, error: String element_bg: String multiValue: String diff --git a/src/themes.ts b/src/themes.ts index c56a3a5d3..1c3dbaa83 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -3,12 +3,13 @@ import { Theme } from "./redux/themeSlice"; export const lightMode: Theme = { background: 'whitesmoke', menu_background: 'snow', - text: '#000', + text: '#3e8ad8', + text_black: '#000', error: '#ed1741', element_bg: 'snow', multiValue: '#e6e6e6', focused:'#e6e6e6', - focus_text: '#000', + focus_text: '#3e8ad8', selected: '#a1a1a1', disabled: 'rgba(0, 0, 0, 0.55)', menuBorder: '1px solid #BBB', @@ -20,7 +21,7 @@ export const lightMode: Theme = { tooltip: '#363636', tooltip_text: '#fff', element_outline: '2px solid transparent', - selected_text: '#000', + selected_text: '#3e8ad8', dropdown_border: '1px solid #ccc', menuButton_outline: '2px solid transparent', button_outline: 'none', @@ -46,6 +47,7 @@ export const darkMode: Theme = { background: '#1C1C1C', menu_background: '#1C1C1C', text: 'rgba(255, 255, 255, 0.87)', + text_black: '#fff', error: 'rgba(237, 23, 65, 0.8)', element_bg: '#2b2b2b', multiValue: '#4a4a4a', @@ -88,6 +90,7 @@ export const highContrastDarkMode: Theme = { background: '#000', menu_background: '#000', text: '#fff', + text_black: '#000', error: '#ED1741', element_bg: 'none', multiValue: '#c4c4c4', @@ -130,6 +133,7 @@ export const highContrastLightMode: Theme = { background: 'snow', menu_background: 'snow', text: '#000', + text_black: '#fff', error: '#a5102d', element_bg: 'none', multiValue: '#2e2e2e', From 9496b555a4ce30a1a2d40156069ae3d2b2c1aa09 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 9 Jun 2023 16:42:04 +0200 Subject: [PATCH 003/107] Redesign - Main Menu Button Shape --- src/cssStyles.tsx | 2 +- src/main/MainMenu.tsx | 6 +++++- src/themes.ts | 14 +++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 5e0c75d38..9e33ee1d9 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -75,7 +75,7 @@ export const flexGapReplacementStyle = (flexGapValue: number, flexDirectionIsRow * CSS for buttons */ export const basicButtonStyle = (theme: Theme) => css({ - borderRadius: '10px', + borderRadius: '5px', cursor: "pointer", // Animation transitionDuration: "0.3s", diff --git a/src/main/MainMenu.tsx b/src/main/MainMenu.tsx index 22df87bd8..64615744c 100644 --- a/src/main/MainMenu.tsx +++ b/src/main/MainMenu.tsx @@ -32,7 +32,7 @@ const MainMenu: React.FC<{}> = () => { const mainMenuStyle = css({ borderRight: `${theme.menuBorder}`, - minWidth: '100px', + minWidth: '120px', display: 'flex', flexDirection: 'column', alignItems: 'center', @@ -143,10 +143,12 @@ const MainMenuButton: React.FC = ({iconName, stateName, ...(activeState === stateName) && { backgroundColor: `${theme.button_color}`, color: `${theme.selected_text}`, + boxShadow: `${theme.boxShadow}`, }, '&:hover': { backgroundColor: `${theme.button_color}`, color: `${theme.selected_text}`, + boxShadow: `${theme.boxShadow}`, }, flexDirection: 'column', }); @@ -158,10 +160,12 @@ const MainMenuButton: React.FC = ({iconName, stateName, ...(activeState === stateName) && { backgroundColor: `${theme.button_color}`, color: `${theme.selected_text}`, + boxShadow: `${theme.boxShadow}`, }, '&:hover': { backgroundColor: `${theme.button_color}`, color: `${theme.selected_text}`, + boxShadow: `${theme.boxShadow}`, }, flexDirection: 'column', }); diff --git a/src/themes.ts b/src/themes.ts index 1c3dbaa83..a7a6a09d7 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -7,13 +7,13 @@ export const lightMode: Theme = { text_black: '#000', error: '#ed1741', element_bg: 'snow', - multiValue: '#e6e6e6', - focused:'#e6e6e6', + multiValue: '#edf6fe', + focused:'#edf6fe', focus_text: '#3e8ad8', selected: '#a1a1a1', disabled: 'rgba(0, 0, 0, 0.55)', menuBorder: '1px solid #BBB', - boxShadow: '0 0 10px rgba(0, 0, 0, 0.3)', + boxShadow: '0 0 2px 2px rgb(195, 218, 238)', singleKey_bg: 'linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(245,245,245,1) 100%)', singleKey_border: 'Gainsboro', invert_wave: 'invert(0%)', @@ -25,7 +25,7 @@ export const lightMode: Theme = { dropdown_border: '1px solid #ccc', menuButton_outline: '2px solid transparent', button_outline: 'none', - button_color: '#e6e6e6', + button_color: '#edf6fe', indicator_color: '#3d3d3d', icon_color: '#000', waveform_filter: 'invert(0%)', @@ -34,13 +34,13 @@ export const lightMode: Theme = { subtitle_segment_bg: 'rgba(0, 0, 0, 0.4)', subtitle_segment_border: '1px solid #363636', subtitle_segment_text: '#fff', - clock_bg: '#e6e6e6', + clock_bg: '#edf6fe', clock_border: '2px solid transparent', clock_hands: '#a1a1a1', clock_focus: '#000', digit_selected: '#000', - text_shadow: '2px 0 #e6e6e6, -2px 0 #e6e6e6, 0 2px #e6e6e6, 0 -2px #e6e6e6,' + - '1px 1px #e6e6e6, -1px -1px #e6e6e6, 1px -1px #e6e6e6, -1px 1px #e6e6e6', + text_shadow: '2px 0 #edf6fe, -2px 0 #edf6fe, 0 2px #edf6fe, 0 -2px #edf6fe,' + + '1px 1px #edf6fe, -1px -1px #edf6fe, 1px -1px #edf6fe, -1px 1px #edf6fe', }; export const darkMode: Theme = { From 05de96894d363fba382f17e8b3f18568e9cde9f3 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 13 Jun 2023 13:49:36 +0200 Subject: [PATCH 004/107] Redesign: Replace fontawesome with react-icons Replaces the fontawesome packages with the react-icons package. React-icons also includes fontawesome icons, but also has feather icons which we want to predominantly use in the redesign. Attempts to replace fontawesome icons with appropriate feather icons. Also grabs a subtitle icon from material-ui- icons. --- package-lock.json | 113 ++++------------------------- package.json | 5 +- src/cssStyles.tsx | 10 ++- src/main/CuttingActions.tsx | 30 +++----- src/main/Discard.tsx | 9 +-- src/main/Error.tsx | 7 +- src/main/Finish.tsx | 9 +-- src/main/FinishMenu.tsx | 18 ++--- src/main/MainContent.tsx | 5 +- src/main/MainMenu.tsx | 30 ++++---- src/main/Save.tsx | 21 +++--- src/main/SubtitleEditor.tsx | 6 +- src/main/SubtitleListEditor.tsx | 20 ++--- src/main/SubtitleSelect.tsx | 7 +- src/main/TheEnd.tsx | 14 +--- src/main/Thumbnail.tsx | 29 ++++---- src/main/Timeline.tsx | 8 +- src/main/TrackSelection.tsx | 24 +++--- src/main/Video.tsx | 27 +++---- src/main/WorkflowConfiguration.tsx | 20 +++-- src/main/WorkflowSelection.tsx | 4 +- src/redux/errorSlice.ts | 4 +- 22 files changed, 154 insertions(+), 266 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1739215ca..607f579ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,6 @@ "@emotion/babel-preset-css-prop": "^11.10.0", "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", - "@fortawesome/fontawesome-svg-core": "^6.4.0", - "@fortawesome/free-regular-svg-icons": "^6.4.0", - "@fortawesome/free-solid-svg-icons": "^6.4.0", - "@fortawesome/react-fontawesome": "^0.2.0", "@iarna/toml": "^2.2.5", "@mui/material": "^5.13.3", "@mui/styles": "^5.13.2", @@ -44,6 +40,7 @@ "react-final-form-listeners": "^1.0.3", "react-hotkeys": "^2.0.0", "react-i18next": "^12.3.1", + "react-icons": "^4.9.0", "react-indiana-drag-scroll": "^2.1.0", "react-player": "git+https://arnei@github.com/Arnei/react-player.git#20fe6c061cf7d71d33d764b4a51c9b9bbb614bf6", "react-redux": "^8.0.7", @@ -2426,63 +2423,6 @@ "@floating-ui/core": "^1.0.1" } }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", - "integrity": "sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==", - "hasInstallScript": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz", - "integrity": "sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.0.tgz", - "integrity": "sha512-ZfycI7D0KWPZtf7wtMFnQxs8qjBXArRzczABuMQqecA/nXohquJ5J/RCR77PmY5qGWkxAZDxpnUFVXKwtY/jPw==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.0.tgz", - "integrity": "sha512-kutPeRGWm8V5dltFP1zGjQOEAzaLZj4StdQhWVZnfGFCvAPVvHh8qk5bRrU4KXnRRRNni5tKQI9PBAdI6MP8nQ==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/react-fontawesome": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", - "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.3" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.10.7", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", @@ -16910,6 +16850,14 @@ } } }, + "node_modules/react-icons": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", + "integrity": "sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-indiana-drag-scroll": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/react-indiana-drag-scroll/-/react-indiana-drag-scroll-2.2.0.tgz", @@ -21626,43 +21574,6 @@ "@floating-ui/core": "^1.0.1" } }, - "@fortawesome/fontawesome-common-types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", - "integrity": "sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==" - }, - "@fortawesome/fontawesome-svg-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz", - "integrity": "sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.0" - } - }, - "@fortawesome/free-regular-svg-icons": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.0.tgz", - "integrity": "sha512-ZfycI7D0KWPZtf7wtMFnQxs8qjBXArRzczABuMQqecA/nXohquJ5J/RCR77PmY5qGWkxAZDxpnUFVXKwtY/jPw==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.0" - } - }, - "@fortawesome/free-solid-svg-icons": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.0.tgz", - "integrity": "sha512-kutPeRGWm8V5dltFP1zGjQOEAzaLZj4StdQhWVZnfGFCvAPVvHh8qk5bRrU4KXnRRRNni5tKQI9PBAdI6MP8nQ==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.0" - } - }, - "@fortawesome/react-fontawesome": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", - "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", - "requires": { - "prop-types": "^15.8.1" - } - }, "@humanwhocodes/config-array": { "version": "0.10.7", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", @@ -32034,6 +31945,12 @@ "html-parse-stringify": "^3.0.1" } }, + "react-icons": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", + "integrity": "sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==", + "requires": {} + }, "react-indiana-drag-scroll": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/react-indiana-drag-scroll/-/react-indiana-drag-scroll-2.2.0.tgz", diff --git a/package.json b/package.json index 2a2bb34a2..cd2016dee 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,6 @@ "@emotion/babel-preset-css-prop": "^11.10.0", "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", - "@fortawesome/fontawesome-svg-core": "^6.4.0", - "@fortawesome/free-regular-svg-icons": "^6.4.0", - "@fortawesome/free-solid-svg-icons": "^6.4.0", - "@fortawesome/react-fontawesome": "^0.2.0", "@iarna/toml": "^2.2.5", "@mui/material": "^5.13.3", "@mui/styles": "^5.13.2", @@ -39,6 +35,7 @@ "react-final-form-listeners": "^1.0.3", "react-hotkeys": "^2.0.0", "react-i18next": "^12.3.1", + "react-icons": "^4.9.0", "react-indiana-drag-scroll": "^2.1.0", "react-player": "git+https://arnei@github.com/Arnei/react-player.git#20fe6c061cf7d71d33d764b4a51c9b9bbb614bf6", "react-redux": "^8.0.7", diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 9e33ee1d9..84ff95517 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -1,7 +1,7 @@ /** * This file contains general css stylings */ -import { css, Global } from '@emotion/react' +import { css, Global, keyframes } from '@emotion/react' import React from "react"; import emotionNormalize from 'emotion-normalize'; import { checkFlexGapSupport } from './util/utilityFunctions'; @@ -146,7 +146,6 @@ export const backOrContinueStyle = css(({ ...(flexGapReplacementStyle(30, false)), boxShadow: `${theme.boxShadow}`, background: `${theme.element_bg}`, - alignItems: 'unset', // overwrite from basicButtonStyle to allow for textOverflow to work placeSelf: 'center', }); @@ -425,3 +424,10 @@ export const subtitleSelectStyle = (theme: Theme) => createTheme({ } } }) + +export const spinningStyle = css({ + animation: `2s linear infinite none ${keyframes({ + "0%": { transform: "rotate(0)" }, + "100%": { transform: "rotate(360deg)" }, +})}`, +}) diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index d82714d9a..9fde76e54 100644 --- a/src/main/CuttingActions.tsx +++ b/src/main/CuttingActions.tsx @@ -2,15 +2,9 @@ import React, { SyntheticEvent } from "react"; import { basicButtonStyle, flexGapReplacementStyle } from '../cssStyles' -import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faCut, - faStepBackward, - faStepForward, - faTrash, - faTrashRestore, - } from "@fortawesome/free-solid-svg-icons"; +import { IconType } from "react-icons"; +import { FiScissors, FiChevronLeft, FiChevronRight} from "react-icons/fi"; +import { FaTrash, FaTrashRestore, } from "react-icons/fa"; import { css } from '@emotion/react' @@ -78,7 +72,7 @@ const CuttingActions: React.FC<{}> = () => {
- = () => { - -
- {/* - */} @@ -122,7 +116,7 @@ const cuttingActionButtonStyle = (theme: Theme) => css({ }); interface cuttingActionsButtonInterface { - iconName: IconProp, + Icon: IconType, actionName: string, actionHandler: (event: KeyboardEvent | SyntheticEvent, action: ActionCreatorWithoutPayload, ref: React.RefObject | undefined) => void, action: ActionCreatorWithoutPayload, @@ -134,7 +128,7 @@ interface cuttingActionsButtonInterface { * A button representing a single action a user can take while cutting * @param param0 */ -const CuttingActionsButton: React.FC = ({iconName, actionName, actionHandler, action, tooltip, ariaLabelText}) => { +const CuttingActionsButton: React.FC = ({Icon, actionName, actionHandler, action, tooltip, ariaLabelText}) => { const ref = React.useRef(null) const theme = useSelector(selectTheme); @@ -148,7 +142,7 @@ const CuttingActionsButton: React.FC = ({iconName actionHandler(event, action, undefined) }}} > - + {actionName}
@@ -182,7 +176,7 @@ const MarkAsDeletedButton : React.FC = ({actionHand actionHandler(event, action, undefined) }}} > - + {isCurrentSegmentAlive ? : }
{isCurrentSegmentAlive ? t('cuttingActions.delete-button') : t("cuttingActions.restore-button")}
diff --git a/src/main/Discard.tsx b/src/main/Discard.tsx index d33ca4d58..6c55b3c57 100644 --- a/src/main/Discard.tsx +++ b/src/main/Discard.tsx @@ -3,10 +3,7 @@ import React from "react"; import { css } from '@emotion/react' import { basicButtonStyle, backOrContinueStyle, navigationButtonStyle, flexGapReplacementStyle} from '../cssStyles' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faChevronLeft, faTimesCircle -} from "@fortawesome/free-solid-svg-icons"; +import { FiChevronLeft, FiXCircle } from "react-icons/fi"; import { useDispatch, useSelector } from 'react-redux'; import { selectFinishState } from '../redux/finishSlice' @@ -41,7 +38,7 @@ const Discard : React.FC<{}> = () => { {t("discard.info-text")}
- +
@@ -70,7 +67,7 @@ const DiscardButton : React.FC<{}> = () => { onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { discard() }}}> - + {t("discard.confirm-button")}
); diff --git a/src/main/Error.tsx b/src/main/Error.tsx index 7b4fa988d..259fb2b34 100644 --- a/src/main/Error.tsx +++ b/src/main/Error.tsx @@ -2,8 +2,7 @@ import React from "react"; import { css } from '@emotion/react' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faFrown } from "@fortawesome/free-solid-svg-icons"; +import { FiFrown } from "react-icons/fi"; import { useSelector } from 'react-redux'; import { selectErrorDetails, selectErrorIcon, selectErrorMessage, selectErrorTitle } from '../redux/errorSlice' @@ -23,7 +22,7 @@ import { useTranslation } from 'react-i18next'; const errorTitle = useSelector(selectErrorTitle) const errorMessage = useSelector(selectErrorMessage) const errorDetails = useSelector(selectErrorDetails) - const errorIcon = useSelector(selectErrorIcon) + const ErrorIcon = useSelector(selectErrorIcon) const detailsStyle = css({ display: 'flex', @@ -43,7 +42,7 @@ import { useTranslation } from 'react-i18next'; return (
{errorTitle ? errorTitle : t("error.generic-message")}
- + {ErrorIcon ? : } {errorMessage}
{errorDetails &&
diff --git a/src/main/Finish.tsx b/src/main/Finish.tsx index c5b477fda..70e886681 100644 --- a/src/main/Finish.tsx +++ b/src/main/Finish.tsx @@ -9,10 +9,7 @@ import WorkflowConfiguration from "./WorkflowConfiguration"; import { css } from '@emotion/react' import { basicButtonStyle } from '../cssStyles' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - IconDefinition -} from "@fortawesome/free-solid-svg-icons"; +import { IconType } from "react-icons"; import { useDispatch, useSelector } from 'react-redux'; import { selectPageNumber, setPageNumber } from '../redux/finishSlice'; @@ -57,7 +54,7 @@ const Finish : React.FC<{}> = () => { /** * Takes you to a different page */ -export const PageButton : React.FC<{pageNumber: number, label: string, iconName: IconDefinition}> = ({pageNumber, label, iconName}) => { +export const PageButton : React.FC<{pageNumber: number, label: string, Icon: IconType}> = ({pageNumber, label, Icon}) => { const theme = useSelector(selectTheme); @@ -83,7 +80,7 @@ export const PageButton : React.FC<{pageNumber: number, label: string, iconName: onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { onPageChange() }}}> - + {label}
); diff --git a/src/main/FinishMenu.tsx b/src/main/FinishMenu.tsx index 9b5203cd5..85aec7f41 100644 --- a/src/main/FinishMenu.tsx +++ b/src/main/FinishMenu.tsx @@ -3,10 +3,8 @@ import React from "react"; import { css } from '@emotion/react' import { basicButtonStyle, flexGapReplacementStyle, tileButtonStyle } from '../cssStyles' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faSave, faFileExport, faTimesCircle, IconDefinition -} from "@fortawesome/free-solid-svg-icons"; +import { IconType } from "react-icons"; +import { FiSave, FiDatabase, FiXCircle } from "react-icons/fi"; import { useDispatch, useSelector } from 'react-redux'; import { setState, setPageNumber, finish } from '../redux/finishSlice' @@ -29,9 +27,9 @@ const FinishMenu : React.FC<{}> = () => { return (
- - - + + +
); } @@ -39,7 +37,7 @@ const FinishMenu : React.FC<{}> = () => { /** * Buttons for the finish menu */ -const FinishMenuButton: React.FC<{iconName: IconDefinition, stateName: finish["value"]}> = ({iconName, stateName}) => { +const FinishMenuButton: React.FC<{Icon: IconType, stateName: finish["value"]}> = ({Icon, stateName}) => { const { t } = useTranslation(); const theme = useSelector(selectTheme) @@ -68,12 +66,12 @@ const FinishMenuButton: React.FC<{iconName: IconDefinition, stateName: finish["v return (
) => { if (event.key === " " || event.key === "Enter") { finish() }}}> - +
{buttonString}
); diff --git a/src/main/MainContent.tsx b/src/main/MainContent.tsx index 1f2a1d73a..d31896880 100644 --- a/src/main/MainContent.tsx +++ b/src/main/MainContent.tsx @@ -9,8 +9,7 @@ import Subtitle from "./Subtitle"; import Finish from "./Finish" import KeyboardControls from "./KeyboardControls"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faTools} from "@fortawesome/free-solid-svg-icons"; +import { FaTools } from "react-icons/fa"; import { css } from '@emotion/react' @@ -146,7 +145,7 @@ const MainContent: React.FC<{}> = () => { ) } else {
- + Placeholder
} diff --git a/src/main/MainMenu.tsx b/src/main/MainMenu.tsx index 64615744c..232f36ae9 100644 --- a/src/main/MainMenu.tsx +++ b/src/main/MainMenu.tsx @@ -2,9 +2,10 @@ import React from "react"; import { css } from '@emotion/react' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faCut, faFilm, faListUl, faPhotoVideo, faSignOutAlt, faGear } from "@fortawesome/free-solid-svg-icons"; -import { faClosedCaptioning } from "@fortawesome/free-regular-svg-icons"; +import { IconType } from "react-icons"; +import { FiScissors, FiFilm, FiFileText, FiCheckSquare, FiSettings} from "react-icons/fi"; +import { FaPhotoVideo } from "react-icons/fa"; +import { MdOutlineSubtitles } from "react-icons/md"; import { useDispatch, useSelector } from 'react-redux' import { setState, selectMainMenuState, mainMenu } from '../redux/mainMenuSlice' @@ -33,6 +34,7 @@ const MainMenu: React.FC<{}> = () => { const mainMenuStyle = css({ borderRight: `${theme.menuBorder}`, minWidth: '120px', + maxWidth: '140px', display: 'flex', flexDirection: 'column', alignItems: 'center', @@ -45,37 +47,37 @@ const MainMenu: React.FC<{}> = () => { return (
diff --git a/src/main/SubtitleListEditor.tsx b/src/main/SubtitleListEditor.tsx index ab2c053e2..2c338fd36 100644 --- a/src/main/SubtitleListEditor.tsx +++ b/src/main/SubtitleListEditor.tsx @@ -1,6 +1,7 @@ import { css, SerializedStyles } from "@emotion/react" -import { faPlus, faTrash } from "@fortawesome/free-solid-svg-icons" -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import { IconType } from "react-icons"; +import { FiPlus} from "react-icons/fi"; +import { FaTrash} from "react-icons/fa"; import { memoize } from "lodash" import React, { useRef } from "react" import { useEffect, useState } from "react" @@ -30,7 +31,6 @@ import { CSSProperties } from "react" import AutoSizer from "react-virtualized-auto-sizer" import { selectTheme, selectThemeState } from "../redux/themeSlice" import { ThemedTooltip } from "./Tooltip" -import { IconProp } from "@fortawesome/fontawesome-svg-core" /** * Displays everything needed to edit subtitles @@ -418,7 +418,7 @@ const SubtitleListSegment = React.memo((props: subtitleListSegmentProps) => { event.stopPropagation() // Prevent video playback due to Space bar press addCueAbove() }}} - icon={faPlus} + Icon={FiPlus} /> { event.stopPropagation() // Prevent video playback due to Space bar press deleteCue() }}} - icon={faTrash} + Icon={FaTrash} /> { event.stopPropagation() // Prevent video playback due to Space bar press addCueBelow() }}} - icon={faPlus} + Icon={FiPlus} />
@@ -453,13 +453,13 @@ const FunctionButton : React.FC<{ tooltipAria: string, onClick: any, onKeyDown: any, - icon: IconProp + Icon: IconType }> = ({ tooltip, tooltipAria, onClick, onKeyDown, - icon + Icon }) => { const theme = useSelector(selectTheme) @@ -475,13 +475,13 @@ const FunctionButton : React.FC<{ return (
- +
) diff --git a/src/main/SubtitleSelect.tsx b/src/main/SubtitleSelect.tsx index 30e41c93c..5dc2da75e 100644 --- a/src/main/SubtitleSelect.tsx +++ b/src/main/SubtitleSelect.tsx @@ -5,8 +5,7 @@ import { settings } from '../config' import { selectSubtitles, setSelectedSubtitleFlavor, setSubtitle } from "../redux/subtitleSlice"; import { useDispatch, useSelector } from "react-redux"; import { setIsDisplayEditView } from "../redux/subtitleSlice"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPlus } from "@fortawesome/free-solid-svg-icons"; +import { FiPlus} from "react-icons/fi"; import { Form } from "react-final-form"; import { Select } from "mui-rff"; import { useState } from "react"; @@ -173,7 +172,7 @@ const SubtitleAddButton: React.FC<{languages: {subFlavor: string, title: string} } const plusIconStyle = css({ - display: isPlusDisplay ? 'block' : 'none' + display: isPlusDisplay ? 'flex' : 'none' }); const subtitleAddFormStyle = css({ @@ -205,7 +204,7 @@ const SubtitleAddButton: React.FC<{languages: {subFlavor: string, title: string} setIsPlusDisplay(false) }}} > - +
= () => { // Init redux variables const endState = useSelector(selectEndState) - const icon = () => { - if (endState === 'discarded') { - return faTimesCircle - } else { - return faCheckCircle - } - } - const text = () => { if (endState === 'discarded') { return t("theEnd.discarded-text") @@ -52,7 +43,7 @@ const TheEnd : React.FC<{}> = () => { return (
- + {endState === 'discarded' ? : }
{text()}
{(endState === 'discarded') && }
@@ -77,7 +68,6 @@ const StartOverButton: React.FC<{}> = () => { onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { reloadPage() }}}> - {/* */} {t("theEnd.startOver-button")}
diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index 6915c5205..6ad0d5f32 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -1,7 +1,6 @@ import { css } from "@emotion/react"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { faCamera, faCopy, faInfoCircle, faTimesCircle, faUpload } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { IconType } from "react-icons"; +import { FiCamera, FiCopy, FiInfo, FiXCircle, FiUpload} from "react-icons/fi"; import React from "react"; import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; @@ -317,7 +316,7 @@ const ThumbnailButtons : React.FC<{ text={t('thumbnail.buttonGenerate')} tooltipText={t('thumbnail.buttonGenerate-tooltip')} ariaLabel={t('thumbnail.buttonGenerate-tooltip-aria')} - icon={faCamera} + Icon={FiCamera} active={true} /> {/* Hidden input field for upload */} @@ -344,7 +343,7 @@ const ThumbnailButtons : React.FC<{ text={t('thumbnail.buttonUseForOtherThumbnails')} tooltipText={t('thumbnail.buttonUseForOtherThumbnails-tooltip')} ariaLabel={t('thumbnail.buttonUseForOtherThumbnails-tooltip-aria')} - icon={faCopy} + Icon={FiCopy} active={(track.thumbnailUri && track.thumbnailUri.startsWith("data") ? true: false)} />
@@ -364,9 +363,9 @@ const ThumbnailButton : React.FC<{ text: string tooltipText: string, ariaLabel: string, - icon: IconProp, + Icon: IconType, active: boolean, -}> = ({handler, text, tooltipText, ariaLabel, icon, active}) => { +}> = ({handler, text, tooltipText, ariaLabel, Icon, active}) => { const theme = useSelector(selectTheme); const ref = React.useRef(null) @@ -389,7 +388,7 @@ const ThumbnailButton : React.FC<{ onClick={clickHandler} onKeyDown={keyHandler} > - + {text} @@ -435,7 +434,7 @@ const AffectAllRow : React.FC<{ return (
- + {t('thumbnail.explanation')}
- + {t('thumbnail.buttonGenerateAll')}
@@ -519,7 +518,7 @@ const ThumbnailButtonsSimple : React.FC<{ text={t('thumbnail.buttonGenerate') + " " + t("thumbnailSimple.from") + " " + generateTrack.flavor.type} tooltipText={t('thumbnail.buttonGenerate-tooltip')} ariaLabel={t('thumbnail.buttonGenerate-tooltip-aria')} - icon={faCamera} + Icon={FiCamera} active={true} key={generateIndex} /> @@ -529,7 +528,7 @@ const ThumbnailButtonsSimple : React.FC<{ text={t('thumbnail.buttonUpload')} tooltipText={t('thumbnail.buttonUpload-tooltip')} ariaLabel={t('thumbnail.buttonUpload-tooltip-aria')} - icon={faUpload} + Icon={FiUpload} active={true} /> {/* Hidden input field for upload */} @@ -548,7 +547,7 @@ const ThumbnailButtonsSimple : React.FC<{ text={t('thumbnail.buttonDiscard')} tooltipText={t('thumbnail.buttonDiscard-tooltip')} ariaLabel={t('thumbnail.buttonDiscard-tooltip-aria')} - icon={faTimesCircle} + Icon={FiXCircle} active={(track.thumbnailUri && track.thumbnailUri.startsWith("data") ? true: false)} />
diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index 72f21d698..585878d64 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -10,8 +10,7 @@ import { selectSegments, selectActiveSegmentIndex, selectDuration, selectVideoURL, selectWaveformImages, setWaveformImages } from '../redux/videoSlice' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faBars, faSpinner } from "@fortawesome/free-solid-svg-icons"; +import { FiMenu, FiLoader} from "react-icons/fi"; import useResizeObserver from "use-resize-observer"; @@ -25,6 +24,7 @@ import { ActionCreatorWithPayload } from '@reduxjs/toolkit'; import { RootState } from '../redux/store'; import { selectTheme } from '../redux/themeSlice'; import { ThemedTooltip } from './Tooltip'; +import { spinningStyle } from '../cssStyles'; /** * A container for visualizing the cutting of the video, as well as for controlling @@ -270,7 +270,7 @@ export const Scrubber: React.FC<{ increase: scrubberKeyMap[handlers.increase.name], decrease: scrubberKeyMap[handlers.decrease.name] })} tabIndex={0}> - + @@ -470,7 +470,7 @@ export const Waveforms: React.FC<{timelineHeight: number}> = ({timelineHeight}) else { return ( <> - +
{t("timeline.generateWaveform-text")}
); diff --git a/src/main/TrackSelection.tsx b/src/main/TrackSelection.tsx index 8fee75113..78771dfb8 100644 --- a/src/main/TrackSelection.tsx +++ b/src/main/TrackSelection.tsx @@ -1,11 +1,9 @@ import React from "react"; import { css } from '@emotion/react' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faInfoCircle, - faTrash, - faTrashRestore, -} from "@fortawesome/free-solid-svg-icons"; + +import { IconType } from "react-icons"; +import { FiInfo } from "react-icons/fi"; +import { FaTrash, FaTrashRestore } from "react-icons/fa"; import ReactPlayer from 'react-player' import { Track } from '../types' @@ -54,7 +52,7 @@ const Description: React.FC<{}> = () => { return ( ); @@ -115,7 +113,7 @@ const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabl t('trackSelection.cannotDeleteTrackTooltip', 'Cannot remove this track from publication.'), t('trackSelection.restoreTrackTooltip', 'Encode and publish this track.') ][deleteStatus]; - const deleteIcon = [faTrash, faTrash, faTrashRestore][deleteStatus]; + const deleteIcon = [FaTrash, FaTrash, FaTrashRestore][deleteStatus]; const trackEnabledChange = () => { dispatch(setTrackEnabled({ id: track.id, @@ -133,7 +131,7 @@ const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabl text={ deleteText } tooltip={ deleteTooltip } handler={ trackEnabledChange } - icon={ deleteIcon } + Icon={ deleteIcon } active={ deleteEnabled } /> ); @@ -142,15 +140,15 @@ const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabl interface selectButtonInterface { handler: any, text: string, - icon: any, + Icon: IconType, tooltip: string, active: boolean, } -const SelectButton : React.FC = ({handler, text, icon, tooltip, active}) => { +const SelectButton : React.FC = ({handler, text, Icon, tooltip, active}) => { const theme = useSelector(selectTheme); - + const buttonStyle = [ active ? basicButtonStyle(theme) : deactivatedButtonStyle, { @@ -179,7 +177,7 @@ const SelectButton : React.FC = ({handler, text, icon, to aria-label={ tooltip } onClick={ clickHandler } onKeyDown={ keyHandler } > - +
{ text }
diff --git a/src/main/Video.tsx b/src/main/Video.tsx index a988e0794..4b7318925 100644 --- a/src/main/Video.tsx +++ b/src/main/Video.tsx @@ -4,8 +4,8 @@ import { css } from '@emotion/react' import { httpRequestState } from '../types' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPlay, faPause, faToggleOn, faToggleOff, faGears} from "@fortawesome/free-solid-svg-icons"; +import { FaPlay, FaPause, FaToggleOn, FaToggleOff, } from "react-icons/fa"; +import { FiMoreHorizontal } from "react-icons/fi"; import { useSelector, useDispatch } from 'react-redux'; import { @@ -55,7 +55,7 @@ export const Video: React.FC<{}> = () => { dispatch(fetchVideoInformation()) } else if (videoURLStatus === 'failed') { if (errorReason === 'workflowActive') { - dispatch(setError({error: true, errorTitle: t("error.workflowActive-errorTitle"), errorMessage: t("error.workflowActive-errorMessage"), errorDetails: undefined, errorIcon: faGears})) + dispatch(setError({error: true, errorTitle: t("error.workflowActive-errorTitle"), errorMessage: t("error.workflowActive-errorMessage"), errorDetails: undefined, errorIcon: FiMoreHorizontal})) } else { dispatch(setError({error: true, errorTitle: undefined, errorMessage: t("video.comError-text"), errorDetails: error, errorIcon: undefined})) } @@ -588,7 +588,7 @@ const PreviewMode: React.FC<{
{t("video.previewButton")}
- + {isPlayPreview ? : } ); @@ -626,15 +626,16 @@ const PlayButton: React.FC<{ return (
- - { switchIsPlaying(event) }} - onKeyDown={(event: React.KeyboardEvent) => { if (event.key === "Enter") { // "Space" is handled by global key - switchIsPlaying(event) - }}} - /> + +
{ switchIsPlaying(event) }} + onKeyDown={(event: React.KeyboardEvent) => { if (event.key === "Enter") { // "Space" is handled by global key + switchIsPlaying(event) + }}}> + {isPlaying ? : } +
); diff --git a/src/main/WorkflowConfiguration.tsx b/src/main/WorkflowConfiguration.tsx index 588b106eb..78c185fba 100644 --- a/src/main/WorkflowConfiguration.tsx +++ b/src/main/WorkflowConfiguration.tsx @@ -1,11 +1,9 @@ import React, { useEffect, useState } from "react"; import { css } from '@emotion/react' -import { basicButtonStyle, backOrContinueStyle, errorBoxStyle, flexGapReplacementStyle } from '../cssStyles' +import { basicButtonStyle, backOrContinueStyle, errorBoxStyle, flexGapReplacementStyle, spinningStyle } from '../cssStyles' -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faTools} from "@fortawesome/free-solid-svg-icons"; -import { faSpinner, faCheck, faExclamationCircle, faChevronLeft, faFileExport } from "@fortawesome/free-solid-svg-icons"; +import { FiLoader, FiCheck, FiAlertCircle, FiChevronLeft, FiDatabase, FiMoreHorizontal} from "react-icons/fi"; import { useDispatch, useSelector } from 'react-redux'; import { selectSegments, selectTracks, setHasChanges as videoSetHasChanges, selectSelectedWorkflowId } from '../redux/videoSlice' @@ -46,11 +44,11 @@ const WorkflowConfiguration : React.FC<{}> = () => { return (

{t("workflowConfig.headline-text")}

- + Placeholder
{t("workflowConfig.satisfied-text")}
- +
@@ -126,16 +124,16 @@ export const SaveAndProcessButton: React.FC<{text: string}> = ({text}) => { }, [metadataStatus]) // Update based on current fetching status - let icon = faFileExport + let Icon = FiDatabase let spin = false if (workflowStatus === 'failed' || metadataStatus === 'failed') { - icon = faExclamationCircle + Icon = FiAlertCircle spin = false } else if (workflowStatus === 'success' && metadataStatus === 'success') { - icon = faCheck + Icon = FiCheck spin = false } else if (workflowStatus === 'loading' || metadataStatus === 'loading') { - icon = faSpinner + Icon = FiLoader spin = true } @@ -153,7 +151,7 @@ export const SaveAndProcessButton: React.FC<{text: string}> = ({text}) => { onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { saveAndProcess() }}}> - + {text}
); diff --git a/src/main/WorkflowSelection.tsx b/src/main/WorkflowSelection.tsx index 0f147fd72..8a21e0ac5 100644 --- a/src/main/WorkflowSelection.tsx +++ b/src/main/WorkflowSelection.tsx @@ -8,7 +8,7 @@ import { selectWorkflows, setSelectedWorkflowIndex } from '../redux/videoSlice' import { selectFinishState, selectPageNumber } from '../redux/finishSlice' import { PageButton } from './Finish' -import { faChevronLeft } from "@fortawesome/free-solid-svg-icons"; +import { FiChevronLeft } from "react-icons/fi"; import { SaveAndProcessButton } from "./WorkflowConfiguration"; import { selectStatus, selectError } from "../redux/workflowPostAndProcessSlice"; import { selectStatus as saveSelectStatus, selectError as saveSelectError } from "../redux/workflowPostSlice"; @@ -91,7 +91,7 @@ const WorkflowSelection : React.FC<{}> = () => { }
- + {/* */} {nextButton}
diff --git a/src/redux/errorSlice.ts b/src/redux/errorSlice.ts index 753b2bf29..bca87c921 100644 --- a/src/redux/errorSlice.ts +++ b/src/redux/errorSlice.ts @@ -1,12 +1,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { IconDefinition } from "@fortawesome/free-solid-svg-icons"; +import { IconType } from "react-icons"; interface error { error: boolean, errorTitle: string | undefined, errorMessage: string, errorDetails: string | undefined, - errorIcon: IconDefinition | undefined, + errorIcon: IconType | undefined, } const initialState: error = { From ebcf6b70da58dac4116a2433eda22bd3f80b3340 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 13 Jun 2023 13:56:02 +0200 Subject: [PATCH 005/107] Redesign: Change background color --- src/themes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/themes.ts b/src/themes.ts index a7a6a09d7..c2865ddc0 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -1,7 +1,7 @@ import { Theme } from "./redux/themeSlice"; export const lightMode: Theme = { - background: 'whitesmoke', + background: '#f3f4f6', menu_background: 'snow', text: '#3e8ad8', text_black: '#000', From fca437d4011bc32e48053166d4a7ff3a054b8cda Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 14 Jun 2023 12:02:19 +0200 Subject: [PATCH 006/107] Redesign: Reorder Cutting components Splits `Video.tsx` into three new files. `Cutting.tsx` now is in charge of the cutting view (before this was awkwardly split between MainContent.tsx and Video.tsx). VideoPlayers and VideoControls now each have their own files so they can be arranged flexibly. --- src/main/Cutting.tsx | 90 ++++++ src/main/MainContent.tsx | 23 +- src/main/SubtitleVideoArea.tsx | 3 +- src/main/Thumbnail.tsx | 3 +- src/main/VideoControls.tsx | 259 +++++++++++++++++ src/main/{Video.tsx => VideoPlayers.tsx} | 341 +---------------------- 6 files changed, 363 insertions(+), 356 deletions(-) create mode 100644 src/main/Cutting.tsx create mode 100644 src/main/VideoControls.tsx rename src/main/{Video.tsx => VideoPlayers.tsx} (50%) diff --git a/src/main/Cutting.tsx b/src/main/Cutting.tsx new file mode 100644 index 000000000..68071e811 --- /dev/null +++ b/src/main/Cutting.tsx @@ -0,0 +1,90 @@ +import React, { useEffect } from "react"; +import CuttingActions from "./CuttingActions" +import Timeline from './Timeline'; +import { fetchVideoInformation, selectCurrentlyAt, selectIsPlaying, selectIsPlayPreview, selectTitle, setClickTriggered, setCurrentlyAt, setIsPlaying, setIsPlayPreview } from '../redux/videoSlice'; +import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import { AppDispatch } from '../redux/store'; +import { httpRequestState } from '../types'; +import { selectTheme } from '../redux/themeSlice'; +import { setError } from '../redux/errorSlice'; +import { selectTitleFromEpisodeDc } from '../redux/metadataSlice'; +import { titleStyle, titleStyleBold } from "../cssStyles"; +import { FiMoreHorizontal } from "react-icons/fi"; +import { css } from "@emotion/react"; +import VideoPlayers from "./VideoPlayers"; +import VideoControls from "./VideoControls"; + +const Cutting: React.FC<{}> = () => { + + const { t } = useTranslation(); + + // Init redux variables + const dispatch = useDispatch() + const videoURLStatus = useSelector((state: { videoState: { status: httpRequestState["status"] } }) => state.videoState.status); + const error = useSelector((state: { videoState: { error: httpRequestState["error"] } }) => state.videoState.error) + const theme = useSelector(selectTheme); + const errorReason = useSelector((state: { videoState: { errorReason: httpRequestState["errorReason"] } }) => state.videoState.errorReason) + + // Try to fetch URL from external API + useEffect(() => { + if (videoURLStatus === 'idle') { + dispatch(fetchVideoInformation()) + } else if (videoURLStatus === 'failed') { + if (errorReason === 'workflowActive') { + dispatch(setError({error: true, errorTitle: t("error.workflowActive-errorTitle"), errorMessage: t("error.workflowActive-errorMessage"), errorDetails: undefined, errorIcon: FiMoreHorizontal})) + } else { + dispatch(setError({error: true, errorTitle: undefined, errorMessage: t("video.comError-text"), errorDetails: error, errorIcon: undefined})) + } + } + }, [videoURLStatus, dispatch, error, t, errorReason]) + + // Style + const cuttingStyle = css({ + display: 'flex', + width: 'auto', + flexDirection: 'column' as const, + justifyContent: 'center', + alignItems: 'center', + padding: '0px', + borderBottom: `${theme.menuBorder}`, + }); + + return ( +
+ + + + + +
+ ) +} + + +const CuttingHeader: React.FC<{}> = () => { + + const title = useSelector(selectTitle) + const metadataTitle = useSelector(selectTitleFromEpisodeDc) + const theme = useSelector(selectTheme); + + return ( +
+ {metadataTitle ? metadataTitle : title} +
+ ); +} + +export default Cutting \ No newline at end of file diff --git a/src/main/MainContent.tsx b/src/main/MainContent.tsx index d31896880..66890a503 100644 --- a/src/main/MainContent.tsx +++ b/src/main/MainContent.tsx @@ -1,8 +1,5 @@ import React from "react"; -import Video from './Video'; -import Timeline from './Timeline'; -import CuttingActions from './CuttingActions'; import Metadata from './Metadata'; import TrackSelection from './TrackSelection'; import Subtitle from "./Subtitle"; @@ -22,14 +19,11 @@ import { flexGapReplacementStyle } from "../cssStyles"; import { useBeforeunload } from 'react-beforeunload'; import { selectHasChanges as videoSelectHasChanges } from "../redux/videoSlice"; import { selectHasChanges as metadataSelectHasChanges} from "../redux/metadataSlice"; -import { - selectIsPlaying, selectCurrentlyAt, - setIsPlaying, setCurrentlyAt, setClickTriggered, -} from '../redux/videoSlice' import { selectHasChanges as selectSubtitleHasChanges } from "../redux/subtitleSlice"; import { selectTheme } from "../redux/themeSlice"; import ThemeSwitcher from "./ThemeSwitcher"; import Thumbnail from "./Thumbnail"; +import Cutting from "./Cutting"; /** * A container for the main functionality @@ -101,9 +95,7 @@ const MainContent: React.FC<{}> = () => { if (mainMenuState === MainMenuStateNames.cutting) { return (
-
) } else if (mainMenuState === MainMenuStateNames.metadata) { @@ -156,16 +148,5 @@ const MainContent: React.FC<{}> = () => { ); }; -const CuttingTimeline : React.FC<{}> = () => { - return ( - - ); -} export default MainContent; diff --git a/src/main/SubtitleVideoArea.tsx b/src/main/SubtitleVideoArea.tsx index 55b4ed37a..a96a4b4c8 100644 --- a/src/main/SubtitleVideoArea.tsx +++ b/src/main/SubtitleVideoArea.tsx @@ -22,11 +22,12 @@ import { Form } from "react-final-form"; import { Select } from "mui-rff"; import { useTranslation } from "react-i18next"; import { OnChange } from 'react-final-form-listeners' -import { VideoControls, VideoPlayer } from "./Video"; import { flexGapReplacementStyle, subtitleSelectStyle } from "../cssStyles"; import { serializeSubtitle } from "../util/utilityFunctions"; import { selectTheme } from "../redux/themeSlice"; import { ThemeProvider } from "@mui/material/styles"; +import { VideoPlayer } from "./VideoPlayers"; +import VideoControls from "./VideoControls"; /** * A part of the subtitle editor that displays a video and related controls diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index 6ad0d5f32..8b9f7de65 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -10,11 +10,12 @@ import { selectTheme, Theme } from "../redux/themeSlice"; import { selectOriginalThumbnails, selectVideos, selectTracks, setHasChanges, setThumbnail, setThumbnails } from "../redux/videoSlice"; import { Track } from "../types"; import Timeline from "./Timeline"; -import { VideoControls, VideoPlayers } from "./Video"; import { selectIsPlaying, selectCurrentlyAt, setIsPlaying, selectIsPlayPreview, setIsPlayPreview, setClickTriggered, setCurrentlyAt } from '../redux/videoSlice' import { ThemedTooltip } from "./Tooltip"; +import VideoPlayers from "./VideoPlayers"; +import VideoControls from "./VideoControls"; /** diff --git a/src/main/VideoControls.tsx b/src/main/VideoControls.tsx new file mode 100644 index 000000000..8b55e165c --- /dev/null +++ b/src/main/VideoControls.tsx @@ -0,0 +1,259 @@ +import React from "react"; + +import { css } from '@emotion/react' + +import { FaPlay, FaPause, FaToggleOn, FaToggleOff, } from "react-icons/fa"; + +import { useSelector, useDispatch } from 'react-redux'; +import { + selectDuration, +} from '../redux/videoSlice' + +import { convertMsToReadableString } from '../util/utilityFunctions' +import { basicButtonStyle, flexGapReplacementStyle } from "../cssStyles"; + +import { GlobalHotKeys, KeyMapOptions } from 'react-hotkeys'; +import { videoPlayerKeyMap } from "../globalKeys"; +import { SyntheticEvent } from "react"; +import { useTranslation } from 'react-i18next'; + +import { RootState } from "../redux/store"; +import { ActionCreatorWithPayload } from "@reduxjs/toolkit"; + +import { ThemedTooltip } from "./Tooltip"; +import { selectTheme, Theme } from "../redux/themeSlice"; + +/** + * Contains controls for manipulating multiple video players at once + * Flexbox magic keeps the play button at the center + */ +const VideoControls: React.FC<{ + selectCurrentlyAt: (state: RootState) => number, + selectIsPlaying: (state: RootState) => boolean, + selectIsPlayPreview: (state: RootState) => boolean, + setIsPlaying: ActionCreatorWithPayload, + setIsPlayPreview: ActionCreatorWithPayload, +}> = ({ + selectCurrentlyAt, + selectIsPlaying, + selectIsPlayPreview, + setIsPlaying, + setIsPlayPreview +}) => { + + const videoControlsRowStyle = css({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + width: '100%', + padding: '20px', + ...(flexGapReplacementStyle(50, false)), + }) + + const leftSideBoxStyle = css({ + width: '100%', + display: 'flex', + justifyContent: 'flex-end' + }) + + const rightSideBoxStyle = css({ + width: '100%', + display: 'flex', + justifyContent: 'flex-start' + }) + + return ( +
+
+ +
+ +
+ +
+
+ ); +} + +/** + * Enable/Disable Preview Mode + */ +const PreviewMode: React.FC<{ + selectIsPlayPreview: (state: RootState) => boolean, + setIsPlayPreview: ActionCreatorWithPayload, +}> = ({ + selectIsPlayPreview, + setIsPlayPreview +}) => { + + const { t } = useTranslation(); + const ref = React.useRef(null) + + // Init redux variables + const dispatch = useDispatch(); + const isPlayPreview = useSelector(selectIsPlayPreview) + const theme = useSelector(selectTheme); + + // Change preview mode from "on" to "off" and vice versa + const switchPlayPreview = (event: KeyboardEvent | SyntheticEvent, ref: React.RefObject | undefined) => { + event.preventDefault() // Prevent page scrolling due to Space bar press + event.stopPropagation() // Prevent video playback due to Space bar press + dispatch(setIsPlayPreview(!isPlayPreview)) + + // Lose focus if clicked by mouse + if (ref) { + ref.current?.blur() + } + } + + // Maps functions to hotkeys + const handlers = { + // preview: switchPlayPreview, + preview: (keyEvent?: KeyboardEvent) => { if(keyEvent) { switchPlayPreview(keyEvent, undefined) } } + } + + const previewModeStyle = css({ + cursor: "pointer", + display: 'flex', + ...(flexGapReplacementStyle(10, false)), + justifyContent: 'center', + alignItems: 'center' + }) + + const switchIconStyle = (theme: Theme) => css({ + cursor: "pointer", + transitionDuration: "0.3s", + transitionProperty: "transform", + "&:hover": { + transform: 'scale(1.05)', + }, + color: `${theme.icon_color}`, + }) + + const previewModeTextStyle = (theme: Theme) => css({ + display: 'inline-block', + flexWrap: 'nowrap', + color: `${theme.text_black}` + }) + + return ( + +
switchPlayPreview(event, ref) } + onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " ") { + switchPlayPreview(event, undefined) + }}}> + +
+ {t("video.previewButton")} +
+ {isPlayPreview ? : } +
+
+ ); +} + +/** + * Start/Pause playing the videos + */ +const PlayButton: React.FC<{ + selectIsPlaying:(state: RootState) => boolean, + setIsPlaying: ActionCreatorWithPayload, +}> = ({ + selectIsPlaying, + setIsPlaying, +}) => { + + const { t } = useTranslation(); + + // Init redux variables + const dispatch = useDispatch(); + const isPlaying = useSelector(selectIsPlaying) + const theme = useSelector(selectTheme); + + // Change play mode from "on" to "off" and vice versa + const switchIsPlaying = (event: KeyboardEvent | SyntheticEvent) => { + event.preventDefault() // Prevent page scrolling due to Space bar press + dispatch(setIsPlaying(!isPlaying)) + } + + // Maps functions to hotkeys + const handlers = { + play: (keyEvent?: KeyboardEvent) => { if(keyEvent) { switchIsPlaying(keyEvent) } } + } + + return ( + +
+ +
{ switchIsPlaying(event) }} + onKeyDown={(event: React.KeyboardEvent) => { if (event.key === "Enter") { // "Space" is handled by global key + switchIsPlaying(event) + }}}> + {isPlaying ? : } +
+
+
+ ); +} + +/** + * Live update for the current time + */ +const TimeDisplay: React.FC<{ + selectCurrentlyAt: (state: RootState) => number, +}> = ({ + selectCurrentlyAt, +}) => { + + const { t } = useTranslation(); + + // Init redux variables + const currentlyAt = useSelector(selectCurrentlyAt) + const duration = useSelector(selectDuration) + const theme = useSelector(selectTheme) + + const timeTextStyle = (theme: Theme) => css({ + display: 'inline-block', + width: '100px', + color: `${theme.text_black}` + }) + + return ( +
+ + + + {" / "} + +
+ {new Date((duration ? duration : 0)).toISOString().substr(11, 12)} +
+
+
+ ); +} + +export default VideoControls diff --git a/src/main/Video.tsx b/src/main/VideoPlayers.tsx similarity index 50% rename from src/main/Video.tsx rename to src/main/VideoPlayers.tsx index 4b7318925..6f5a5f84f 100644 --- a/src/main/Video.tsx +++ b/src/main/VideoPlayers.tsx @@ -2,103 +2,27 @@ import React, { useState, useRef, useEffect, useImperativeHandle } from "react"; import { css } from '@emotion/react' -import { httpRequestState } from '../types' - -import { FaPlay, FaPause, FaToggleOn, FaToggleOff, } from "react-icons/fa"; -import { FiMoreHorizontal } from "react-icons/fi"; - import { useSelector, useDispatch } from 'react-redux'; import { - selectIsPlaying, selectCurrentlyAt, selectCurrentlyAtInSeconds, setIsPlaying, - fetchVideoInformation, selectVideoURL, selectVideoCount, selectDurationInSeconds, selectTitle, - setPreviewTriggered, selectPreviewTriggered, selectIsPlayPreview, setIsPlayPreview, setAspectRatio, selectAspectRatio, selectDuration, setClickTriggered, selectClickTriggered, setCurrentlyAt + selectIsPlaying, selectCurrentlyAtInSeconds, setIsPlaying, + selectVideoURL, selectVideoCount, selectDurationInSeconds, + setPreviewTriggered, selectPreviewTriggered, setAspectRatio, selectAspectRatio, setClickTriggered, selectClickTriggered, setCurrentlyAt } from '../redux/videoSlice' import ReactPlayer, { Config } from 'react-player' -import { roundToDecimalPlace, convertMsToReadableString } from '../util/utilityFunctions' -import { basicButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold } from "../cssStyles"; +import { roundToDecimalPlace } from '../util/utilityFunctions' -import { GlobalHotKeys, KeyMapOptions } from 'react-hotkeys'; -import { videoPlayerKeyMap } from "../globalKeys"; -import { SyntheticEvent } from "react"; import { useTranslation } from 'react-i18next'; -import { selectTitleFromEpisodeDc } from "../redux/metadataSlice"; -import { setError } from "../redux/errorSlice"; import { sleep } from './../util/utilityFunctions' -import { AppDispatch, RootState } from "../redux/store"; +import { RootState } from "../redux/store"; import { ActionCreatorWithPayload } from "@reduxjs/toolkit"; -import { ThemedTooltip } from "./Tooltip"; -import { selectTheme, Theme } from "../redux/themeSlice"; - -/** - * Container for the videos and their controls - * TODO: Move fetching to a more central part of the app - */ -export const Video: React.FC<{}> = () => { - - const { t } = useTranslation(); +import { selectTheme } from "../redux/themeSlice"; - // Init redux variables - const dispatch = useDispatch() - const videoURLStatus = useSelector((state: { videoState: { status: httpRequestState["status"] } }) => state.videoState.status); - const error = useSelector((state: { videoState: { error: httpRequestState["error"] } }) => state.videoState.error) - const theme = useSelector(selectTheme); - const errorReason = useSelector((state: { videoState: { errorReason: httpRequestState["errorReason"] } }) => state.videoState.errorReason) - - // Try to fetch URL from external API - useEffect(() => { - if (videoURLStatus === 'idle') { - dispatch(fetchVideoInformation()) - } else if (videoURLStatus === 'failed') { - if (errorReason === 'workflowActive') { - dispatch(setError({error: true, errorTitle: t("error.workflowActive-errorTitle"), errorMessage: t("error.workflowActive-errorMessage"), errorDetails: undefined, errorIcon: FiMoreHorizontal})) - } else { - dispatch(setError({error: true, errorTitle: undefined, errorMessage: t("video.comError-text"), errorDetails: error, errorIcon: undefined})) - } - } - }, [videoURLStatus, dispatch, error, t, errorReason]) - - // Update based on current fetching status - // let content - // if (videoURLStatus === 'loading') { - // content =
Loading...
- // } else if (videoURLStatus === 'success') { - // content = ""//
Success...
- // } else if (videoURLStatus === 'failed') { - // content =
{error}
- // } - - // Style - const videoAreaStyle = css({ - display: 'flex', - width: 'auto', - flexDirection: 'column' as const, - justifyContent: 'center', - alignItems: 'center', - padding: '0px', - borderBottom: `${theme.menuBorder}`, - }); - - return ( -
- - - -
- ); -}; - -export const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, widthInPercent=100}) => { +const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, widthInPercent=100}) => { const videoURLs = useSelector(selectVideoURL) const videoCount = useSelector(selectVideoCount) @@ -449,253 +373,4 @@ export const VideoPlayer = React.forwardRef( // ); }); -/** - * Contains controls for manipulating multiple video players at once - * Flexbox magic keeps the play button at the center - */ -export const VideoControls: React.FC<{ - selectCurrentlyAt: (state: RootState) => number, - selectIsPlaying: (state: RootState) => boolean, - selectIsPlayPreview: (state: RootState) => boolean, - setIsPlaying: ActionCreatorWithPayload, - setIsPlayPreview: ActionCreatorWithPayload, -}> = ({ - selectCurrentlyAt, - selectIsPlaying, - selectIsPlayPreview, - setIsPlaying, - setIsPlayPreview -}) => { - - const videoControlsRowStyle = css({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - width: '100%', - padding: '20px', - ...(flexGapReplacementStyle(50, false)), - }) - - const leftSideBoxStyle = css({ - width: '100%', - display: 'flex', - justifyContent: 'flex-end' - }) - - const rightSideBoxStyle = css({ - width: '100%', - display: 'flex', - justifyContent: 'flex-start' - }) - - return ( -
-
- -
- -
- -
-
- ); -} - -/** - * Enable/Disable Preview Mode - */ -const PreviewMode: React.FC<{ - selectIsPlayPreview: (state: RootState) => boolean, - setIsPlayPreview: ActionCreatorWithPayload, -}> = ({ - selectIsPlayPreview, - setIsPlayPreview -}) => { - - const { t } = useTranslation(); - const ref = React.useRef(null) - - // Init redux variables - const dispatch = useDispatch(); - const isPlayPreview = useSelector(selectIsPlayPreview) - const theme = useSelector(selectTheme); - - // Change preview mode from "on" to "off" and vice versa - const switchPlayPreview = (event: KeyboardEvent | SyntheticEvent, ref: React.RefObject | undefined) => { - event.preventDefault() // Prevent page scrolling due to Space bar press - event.stopPropagation() // Prevent video playback due to Space bar press - dispatch(setIsPlayPreview(!isPlayPreview)) - - // Lose focus if clicked by mouse - if (ref) { - ref.current?.blur() - } - } - - // Maps functions to hotkeys - const handlers = { - // preview: switchPlayPreview, - preview: (keyEvent?: KeyboardEvent) => { if(keyEvent) { switchPlayPreview(keyEvent, undefined) } } - } - - const previewModeStyle = css({ - cursor: "pointer", - display: 'flex', - ...(flexGapReplacementStyle(10, false)), - justifyContent: 'center', - alignItems: 'center' - }) - - const switchIconStyle = (theme: Theme) => css({ - cursor: "pointer", - transitionDuration: "0.3s", - transitionProperty: "transform", - "&:hover": { - transform: 'scale(1.05)', - }, - color: `${theme.icon_color}`, - }) - - const previewModeTextStyle = (theme: Theme) => css({ - display: 'inline-block', - flexWrap: 'nowrap', - color: `${theme.text_black}` - }) - - return ( - -
switchPlayPreview(event, ref) } - onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " ") { - switchPlayPreview(event, undefined) - }}}> - -
- {t("video.previewButton")} -
- {isPlayPreview ? : } -
-
- ); -} - -/** - * Start/Pause playing the videos - */ -const PlayButton: React.FC<{ - selectIsPlaying:(state: RootState) => boolean, - setIsPlaying: ActionCreatorWithPayload, -}> = ({ - selectIsPlaying, - setIsPlaying, -}) => { - - const { t } = useTranslation(); - - // Init redux variables - const dispatch = useDispatch(); - const isPlaying = useSelector(selectIsPlaying) - const theme = useSelector(selectTheme); - - // Change play mode from "on" to "off" and vice versa - const switchIsPlaying = (event: KeyboardEvent | SyntheticEvent) => { - event.preventDefault() // Prevent page scrolling due to Space bar press - dispatch(setIsPlaying(!isPlaying)) - } - - // Maps functions to hotkeys - const handlers = { - play: (keyEvent?: KeyboardEvent) => { if(keyEvent) { switchIsPlaying(keyEvent) } } - } - - return ( - -
- -
{ switchIsPlaying(event) }} - onKeyDown={(event: React.KeyboardEvent) => { if (event.key === "Enter") { // "Space" is handled by global key - switchIsPlaying(event) - }}}> - {isPlaying ? : } -
-
-
- ); -} - -/** - * Live update for the current time - */ -const TimeDisplay: React.FC<{ - selectCurrentlyAt: (state: RootState) => number, -}> = ({ - selectCurrentlyAt, -}) => { - - const { t } = useTranslation(); - - // Init redux variables - const currentlyAt = useSelector(selectCurrentlyAt) - const duration = useSelector(selectDuration) - const theme = useSelector(selectTheme) - - const timeTextStyle = (theme: Theme) => css({ - display: 'inline-block', - width: '100px', - color: `${theme.text_black}` - }) - - return ( -
- - - - {" / "} - -
- {new Date((duration ? duration : 0)).toISOString().substr(11, 12)} -
-
-
- ); -} - -/** - * Displays elements above the video, e.g. title - */ -const VideoHeader: React.FC<{}> = () => { - - const title = useSelector(selectTitle) - const metadataTitle = useSelector(selectTitleFromEpisodeDc) - const theme = useSelector(selectTheme); - - return ( -
- {metadataTitle ? metadataTitle : title} -
- ); -} - -export default Video; +export default VideoPlayers From 5ff94611da3bd1efb3727e00acba2fd7fdc6c04b Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 14 Jun 2023 16:55:15 +0200 Subject: [PATCH 007/107] Redesign: General cutting view appearance Works on the appearance of the cutting view, mostly concerned with boxes and boxshadows. --- src/main/Cutting.tsx | 44 +++++++++++++++++++++++++-------------- src/main/VideoPlayers.tsx | 6 +++++- src/redux/themeSlice.ts | 1 + src/themes.ts | 8 +++++-- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/main/Cutting.tsx b/src/main/Cutting.tsx index 8084f52e0..059685880 100644 --- a/src/main/Cutting.tsx +++ b/src/main/Cutting.tsx @@ -52,28 +52,40 @@ const Cutting: React.FC = () => { justifyContent: 'center', alignItems: 'center', padding: '0px', - borderBottom: `${theme.menuBorder}`, }); + const bottomStyle = css({ + width: '100%', + background: `${theme.menu_background}`, + borderRadius: '5px', + boxShadow: `${theme.boxShadow_tiles}`, + marginTop: '10px', + boxSizing: "border-box", + padding: '10px', + }) + return (
- - - +
+ + + +
) } diff --git a/src/main/VideoPlayers.tsx b/src/main/VideoPlayers.tsx index 9c84b9473..511fb1a03 100644 --- a/src/main/VideoPlayers.tsx +++ b/src/main/VideoPlayers.tsx @@ -24,6 +24,8 @@ import { selectTheme } from "../redux/themeSlice"; const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, widthInPercent = 100}) => { + const theme = useSelector(selectTheme); + const videoURLs = useSelector(selectVideoURL) const videoCount = useSelector(selectVideoCount) @@ -33,6 +35,7 @@ const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, wid justifyContent: 'center', alignItems: 'center', width: widthInPercent + '%', + boxShadow: `${theme.boxShadow_tiles}`, }); // Initialize video players @@ -316,7 +319,8 @@ export const VideoPlayer = React.forwardRef( const playerWrapper = css({ position: 'relative', width: '100%', - paddingTop: aspectRatio + '%', + paddingTop: `min(${aspectRatio + '%'}, ${'30vh'})`, + overflow: 'hidden', }); const reactPlayerStyle = css({ diff --git a/src/redux/themeSlice.ts b/src/redux/themeSlice.ts index 5aa4a0737..f5393b30a 100644 --- a/src/redux/themeSlice.ts +++ b/src/redux/themeSlice.ts @@ -15,6 +15,7 @@ export interface Theme { disabled: string menuBorder: string boxShadow: string + boxShadow_tiles: string singleKey_bg: string singleKey_border: string invert_wave: string diff --git a/src/themes.ts b/src/themes.ts index 06e6ba13c..96d4913e1 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -16,6 +16,7 @@ export const lightMode: Theme = { disabled: 'rgba(0, 0, 0, 0.55)', menuBorder: '1px solid #BBB', boxShadow: '0 0 2px 2px rgb(195, 218, 238)', + boxShadow_tiles: '0 5px 10px 0px rgba(150, 150, 150, 0.5)', singleKey_bg: 'linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(245,245,245,1) 100%)', singleKey_border: 'Gainsboro', invert_wave: 'invert(0%)', @@ -59,6 +60,7 @@ export const darkMode: Theme = { disabled: 'rgba(255, 255, 255, 0.5)', menuBorder: '1px solid #5d5d5d', boxShadow: '0 0 5px rgba(255, 255, 255, 0.3)', + boxShadow_tiles: '0 5px 10px 0px rgba(100, 100, 100, 0.3)', singleKey_bg: 'linear-gradient(180deg, rgba(40,40,40,1) 0%, rgba(30,30,30,1) 100%)', singleKey_border: '#404040', invert_wave: 'invert(100%)', @@ -92,7 +94,7 @@ export const highContrastDarkMode: Theme = { background: '#000', menu_background: '#000', text: '#fff', - text_black: '#000', + text_black: '#fff', error: '#ED1741', element_bg: 'none', multiValue: '#c4c4c4', @@ -102,6 +104,7 @@ export const highContrastDarkMode: Theme = { disabled: 'rgba(255, 255, 255, 0.6)', menuBorder: '2px solid #fff', boxShadow: '0 0 0 rgba(255, 255, 255, 0.3)', + boxShadow_tiles: '0 0 0 rgba(255, 255, 255, 0.3)', singleKey_bg: 'none', singleKey_border: '#fff', invert_wave: 'invert(100%)', @@ -135,7 +138,7 @@ export const highContrastLightMode: Theme = { background: 'snow', menu_background: 'snow', text: '#000', - text_black: '#fff', + text_black: '#000', error: '#a5102d', element_bg: 'none', multiValue: '#2e2e2e', @@ -145,6 +148,7 @@ export const highContrastLightMode: Theme = { disabled: 'rgba(0, 0, 0, 0.6)', menuBorder: '2px solid #000', boxShadow: '0 0 0 rgba(0, 0, 0, 0.3)', + boxShadow_tiles: '0 0 0 rgba(0, 0, 0, 0.3)', singleKey_bg: 'none', singleKey_border: '#000', invert_wave: 'invert(0%)', From 52638d852516eaa117fb380ff5c462d7779c2b0f Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 10:38:56 +0200 Subject: [PATCH 008/107] Redesign: Timeline color Attempts to fit the timeline to the colors of the redesign. Uses a hacky filter. --- src/main/Timeline.tsx | 25 ++++++++++++------------- src/themes.ts | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index e434eb89e..6f0f4c476 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -304,23 +304,23 @@ export const SegmentsList: React.FC<{ */ const bgColor = (deleted: boolean, active: boolean) => { if (!deleted && !active) { - return 'rgba(0, 0, 255, 0.4)' + return 'rgba(137, 195, 252, 0.4)' } else if (deleted && !active) { return `repeating-linear-gradient( - -45deg, - rgba(255, 45, 45, 0.4), - rgba(255, 45, 45, 0.4) 10px, - rgba(255, 0, 0, 0.4) 10px, - rgba(255, 0, 0, 0.4) 20px);` + -35deg, + rgba(255, 95, 95, 0.4), + rgba(255, 95, 95, 0.4) 5px, + rgba(200, 0, 0, 0.4) 5px, + rgba(200, 0, 0, 0.4) 10px);` } else if (!deleted && active) { - return 'rgba(0, 0, 200, 0.4)' + return 'rgba(78, 163, 252, 0.4)' } else if (deleted && active) { return `repeating-linear-gradient( - -45deg, - rgba(200, 45, 45, 0.4), - rgba(200, 45, 45, 0.4) 10px, - rgba(200, 0, 0, 0.4) 10px, - rgba(200, 0, 0, 0.4) 20px);` + -35deg, + rgba(255, 65, 65, 0.4), + rgba(255, 65, 65, 0.4) 5px, + rgba(180, 0, 0, 0.4) 5px, + rgba(180, 0, 0, 0.4) 10px);` } } @@ -338,7 +338,6 @@ export const SegmentsList: React.FC<{ tabIndex={tabable ? 0 : -1} css={{ background: bgColor(segment.deleted, styleByActiveSegment ? activeSegmentIndex === index : false), - borderRadius: '5px', borderStyle: styleByActiveSegment ? (activeSegmentIndex === index ? 'dashed' : 'solid') : 'solid', borderColor: 'white', borderWidth: '1px', diff --git a/src/themes.ts b/src/themes.ts index 96d4913e1..bfe67d640 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -31,8 +31,8 @@ export const lightMode: Theme = { button_color: '#edf6fe', indicator_color: '#3d3d3d', icon_color: '#000', - waveform_filter: 'invert(0%)', - waveform_bg: '#fff', + waveform_filter: 'invert(44%) sepia(8%) saturate(3893%) hue-rotate(169deg) brightness(99%) contrast(90%)', // All this just to turn the black part of the waveform image blue. Generated with: https://isotropic.co/tool/hex-color-to-css-filter/ + waveform_bg: '', scrubber: '1px solid transparent', subtitle_segment_bg: 'rgba(0, 0, 0, 0.4)', subtitle_segment_border: '1px solid #363636', From bd35e07cbc2aff66466af61ab85616fa2a883b50 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 14:09:38 +0200 Subject: [PATCH 009/107] Redesign: Scrubber basic idea Reshapes the scrubber to fit the basic idea of the redesign. For a better fit somebody with artistic skills would have to create an SVG. --- src/main/Timeline.tsx | 20 ++++++++++---------- src/themes.ts | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index 6f0f4c476..7bb0e09a0 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -191,35 +191,35 @@ export const Scrubber: React.FC<{ } const scrubberStyle = css({ - backgroundColor: `${theme.text}`, + backgroundColor: `${theme.scrubber}`, height: timelineHeight - 10 + 'px', // TODO: CHECK IF height: '100%', width: '1px', position: 'absolute', zIndex: 2, - boxShadow: `${theme.boxShadow}`, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', alignItems: 'center', - outline: `${theme.scrubber}`, }); const scrubberDragHandleStyle = css({ // Base style - background: `${theme.text}`, + background: `${theme.menu_background}`, display: "inline-block", - height: "10px", + height: "20px", position: "relative", width: "20px", + borderRadius: '5px', + boxShadow: `${theme.boxShadow_tiles}`, "&:after": { - borderTop: `10px solid ${theme.text}`, + borderTop: `10px solid ${theme.menu_background}`, borderLeft: '10px solid transparent', borderRight: '10px solid transparent', content: '""', height: 0, left: 0, position: "absolute", - top: "10px", + top: "17px", width: 0, }, // Animation @@ -238,9 +238,9 @@ export const Scrubber: React.FC<{ }) const scrubberDragHandleIconStyle = css({ - transform: 'scaleY(0.7) rotate(90deg)', - paddingRight: '5px', - color: `${theme.background}`, + paddingLeft: '2px', + paddingTop: '2px', + color: `${theme.text}`, }) // // Possible TODO: Find a way to use ariaLive in a way that only the latest change is announced diff --git a/src/themes.ts b/src/themes.ts index bfe67d640..723f4cfd9 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -33,7 +33,7 @@ export const lightMode: Theme = { icon_color: '#000', waveform_filter: 'invert(44%) sepia(8%) saturate(3893%) hue-rotate(169deg) brightness(99%) contrast(90%)', // All this just to turn the black part of the waveform image blue. Generated with: https://isotropic.co/tool/hex-color-to-css-filter/ waveform_bg: '', - scrubber: '1px solid transparent', + scrubber: '#3f73a7', subtitle_segment_bg: 'rgba(0, 0, 0, 0.4)', subtitle_segment_border: '1px solid #363636', subtitle_segment_text: '#fff', @@ -77,7 +77,7 @@ export const darkMode: Theme = { icon_color: 'rgba(255, 255, 255, 0.87)', waveform_filter: 'invert(11%)', waveform_bg: '#fff', - scrubber: '1px solid transparent', + scrubber: '#fff', subtitle_segment_bg: 'rgba(0, 0, 0, 0.4)', subtitle_segment_border: '1px solid #dddddd', subtitle_segment_text: '#fff', @@ -121,7 +121,7 @@ export const highContrastDarkMode: Theme = { icon_color: '#a6ffea', waveform_filter: 'invert(100%)', waveform_bg: '#80B8AC', - scrubber: '1px solid transparent', + scrubber: '#fff', subtitle_segment_bg: 'none', subtitle_segment_border: '2px solid #fff', subtitle_segment_text: '#fff', @@ -165,7 +165,7 @@ export const highContrastLightMode: Theme = { icon_color: '#000099', waveform_filter: 'invert(0%)', waveform_bg: '#fff', - scrubber: '1px solid transparent', + scrubber: '#000', subtitle_segment_bg: 'none', subtitle_segment_border: '2px solid #000', subtitle_segment_text: '#000', From fcefd54ba4e4ef6b3a6aea683dedbafe33b666cb Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 14:38:09 +0200 Subject: [PATCH 010/107] Redesign: Cutting Buttons Now centered and boxShado-less! --- src/main/CuttingActions.tsx | 75 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index 30b2da293..4860aede4 100644 --- a/src/main/CuttingActions.tsx +++ b/src/main/CuttingActions.tsx @@ -58,49 +58,48 @@ const CuttingActions: React.FC = () => { const cuttingStyle = css({ display: 'flex', flexDirection: 'row' as const, - justifyContent: 'space-between', - ...(flexGapReplacementStyle(30, true)), + justifyContent: 'center', + alignItems: 'center', + // ...(flexGapReplacementStyle(30, true)), }) - const blockStyle = css({ - display: 'flex', - flexDirection: 'row' as const, - ...(flexGapReplacementStyle(30, true)), + const verticalLineStyle = css({ + borderLeft: '2px solid #DDD;', + height: '32px', }) return (
-
- - - - -
-
- {/* - */} -
+ +
+ +
+ +
+ + {/* + */}
); @@ -111,8 +110,8 @@ const CuttingActions: React.FC = () => { */ const cuttingActionButtonStyle = (theme: Theme) => css({ padding: '16px', - boxShadow: `${theme.boxShadow}`, - background: `${theme.element_bg}` + // boxShadow: `${theme.boxShadow}`, + // background: `${theme.element_bg}` }); interface cuttingActionsButtonInterface { From 909878272dbda4f6079e07d4af2ede0668203973 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 16:02:53 +0200 Subject: [PATCH 011/107] Redesign: VideoControls Layout Also aim at improving the general layout of the cutting view again. --- src/main/Cutting.tsx | 2 +- src/main/CuttingActions.tsx | 11 ++++----- src/main/Timeline.tsx | 13 ++++++----- src/main/VideoControls.tsx | 45 ++++++++++++++++++++++++++----------- src/themes.ts | 2 +- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/main/Cutting.tsx b/src/main/Cutting.tsx index 059685880..9e0eab7ae 100644 --- a/src/main/Cutting.tsx +++ b/src/main/Cutting.tsx @@ -59,7 +59,7 @@ const Cutting: React.FC = () => { background: `${theme.menu_background}`, borderRadius: '5px', boxShadow: `${theme.boxShadow_tiles}`, - marginTop: '10px', + marginTop: '24px', boxSizing: "border-box", padding: '10px', }) diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index 4860aede4..5077c1af8 100644 --- a/src/main/CuttingActions.tsx +++ b/src/main/CuttingActions.tsx @@ -1,6 +1,6 @@ import React, { SyntheticEvent } from "react"; -import { basicButtonStyle, flexGapReplacementStyle } from '../cssStyles' +import { basicButtonStyle } from '../cssStyles' import { IconType } from "react-icons"; import { FiScissors, FiChevronLeft, FiChevronRight} from "react-icons/fi"; @@ -17,7 +17,7 @@ import { cuttingKeyMap } from "../globalKeys"; import { ActionCreatorWithoutPayload } from "@reduxjs/toolkit"; import { useTranslation } from 'react-i18next'; -import { selectTheme, Theme } from "../redux/themeSlice"; +import { selectTheme } from "../redux/themeSlice"; import { ThemedTooltip } from "./Tooltip"; /** @@ -60,6 +60,7 @@ const CuttingActions: React.FC = () => { flexDirection: 'row' as const, justifyContent: 'center', alignItems: 'center', + padding: '10px', // ...(flexGapReplacementStyle(30, true)), }) @@ -108,7 +109,7 @@ const CuttingActions: React.FC = () => { /** * CSS for cutting buttons */ -const cuttingActionButtonStyle = (theme: Theme) => css({ +const cuttingActionButtonStyle = css({ padding: '16px', // boxShadow: `${theme.boxShadow}`, // background: `${theme.element_bg}` @@ -133,7 +134,7 @@ const CuttingActionsButton: React.FC = ({Icon, ac return ( -
actionHandler(event, action, ref)} @@ -166,7 +167,7 @@ const MarkAsDeletedButton : React.FC = ({actionHand return ( -
-
+
@@ -192,7 +192,7 @@ export const Scrubber: React.FC<{ const scrubberStyle = css({ backgroundColor: `${theme.scrubber}`, - height: timelineHeight - 10 + 'px', // TODO: CHECK IF height: '100%', + height: timelineHeight + 20 + 'px', // TODO: CHECK IF height: '100%', width: '1px', position: 'absolute', zIndex: 2, @@ -200,6 +200,7 @@ export const Scrubber: React.FC<{ flexDirection: 'column', justifyContent: 'space-between', alignItems: 'center', + top: '-20px', }); const scrubberDragHandleStyle = css({ @@ -343,7 +344,7 @@ export const SegmentsList: React.FC<{ borderWidth: '1px', boxSizing: 'border-box', width: ((segment.end - segment.start) / duration) * 100 + '%', - height: timelineHeight - 20 + 'px', // CHECK IF 100% + height: timelineHeight + 'px', // CHECK IF 100% zIndex: 1, }}>
@@ -355,7 +356,7 @@ export const SegmentsList: React.FC<{ const segmentsStyle = css({ display: 'flex', flexDirection: 'row', - paddingTop: '10px', + // paddingTop: '10px', height: '100%', }) @@ -389,8 +390,8 @@ export const Waveforms: React.FC<{timelineHeight: number}> = ({timelineHeight}) justifyContent: 'center', ...(images.length <= 0) && {alignItems: 'center'}, // Only center during loading width: '100%', - height: timelineHeight - 20 + 'px', // CHECK IF height: '100%', - paddingTop: '10px', + height: timelineHeight + 'px', // CHECK IF height: '100%', + // paddingTop: '10px', filter: `${theme.invert_wave}`, color: `${theme.inverted_text}`, }); diff --git a/src/main/VideoControls.tsx b/src/main/VideoControls.tsx index e6d555f5f..668c7d868 100644 --- a/src/main/VideoControls.tsx +++ b/src/main/VideoControls.tsx @@ -2,7 +2,8 @@ import React from "react"; import { css } from '@emotion/react' -import { FaPlay, FaPause, FaToggleOn, FaToggleOff } from "react-icons/fa"; +import { FaToggleOn, FaToggleOff } from "react-icons/fa"; +import { FiPlay, FiPause } from "react-icons/fi"; import { useSelector, useDispatch } from 'react-redux'; import { @@ -41,15 +42,20 @@ const VideoControls: React.FC<{ setIsPlayPreview }) => { + const theme = useSelector(selectTheme); + const videoControlsRowStyle = css({ + background: `${theme.background}`, + outline: `5px solid ${theme.background}`, // Fake the box bigger than it actually is display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', overflow: 'hidden', width: '100%', - padding: '20px', - ...(flexGapReplacementStyle(50, false)), + paddingTop: '10px', + paddingBottom: '10px', + ...(flexGapReplacementStyle(30, false)), }) const leftSideBoxStyle = css({ @@ -67,9 +73,8 @@ const VideoControls: React.FC<{ return (
-
-
@@ -130,14 +136,15 @@ const PreviewMode: React.FC<{ alignItems: 'center' }) - const switchIconStyle = (theme: Theme) => css({ + const switchIconStyle = css({ cursor: "pointer", transitionDuration: "0.3s", transitionProperty: "transform", "&:hover": { transform: 'scale(1.05)', }, - color: `${theme.icon_color}`, + color: `#3073b8`, + fontSize: '28px', }) const previewModeTextStyle = (theme: Theme) => css({ @@ -163,7 +170,7 @@ const PreviewMode: React.FC<{
{t("video.previewButton")}
- {isPlayPreview ? : } + {isPlayPreview ? : }
); @@ -198,18 +205,30 @@ const PlayButton: React.FC<{ play: (keyEvent?: KeyboardEvent) => { if (keyEvent) { switchIsPlaying(keyEvent) } } } + const playButtonStyle = css({ + justifySelf: 'center', + outline: 'none', + color: `${theme.icon_color}`, + + background: '#3073b8', + borderRadius: '50%', + width: '50px', + height: '50px', + boxShadow: `${theme.boxShadow_tiles}` + }) + return (
-
{ switchIsPlaying(event) }} onKeyDown={(event: React.KeyboardEvent) => { if (event.key === "Enter") { // "Space" is handled by global key switchIsPlaying(event) } }}> - {isPlaying ? : } + {isPlaying ? : }
diff --git a/src/themes.ts b/src/themes.ts index 723f4cfd9..e0c1277be 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -30,7 +30,7 @@ export const lightMode: Theme = { button_outline: 'none', button_color: '#edf6fe', indicator_color: '#3d3d3d', - icon_color: '#000', + icon_color: '#fff', waveform_filter: 'invert(44%) sepia(8%) saturate(3893%) hue-rotate(169deg) brightness(99%) contrast(90%)', // All this just to turn the black part of the waveform image blue. Generated with: https://isotropic.co/tool/hex-color-to-css-filter/ waveform_bg: '', scrubber: '#3f73a7', From 1d301ab0e8cdd6117d0bf491e1b9906aee412ac3 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 16:17:31 +0200 Subject: [PATCH 012/107] Redesign: Rounded edges for videos The videos now have rounded edges. Finally. --- src/main/SubtitleVideoArea.tsx | 2 ++ src/main/VideoPlayers.tsx | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/SubtitleVideoArea.tsx b/src/main/SubtitleVideoArea.tsx index f42d77c4a..1ac5db3db 100644 --- a/src/main/SubtitleVideoArea.tsx +++ b/src/main/SubtitleVideoArea.tsx @@ -119,6 +119,8 @@ const SubtitleVideoArea : React.FC = () => { url={getTrackURI()} isPrimary={true} subtitleUrl={subtitleUrl} + first={true} + last={true} selectIsPlaying={selectIsPlaying} selectCurrentlyAtInSeconds={selectCurrentlyAtInSeconds} selectPreviewTriggered={selectPreviewTriggered} diff --git a/src/main/VideoPlayers.tsx b/src/main/VideoPlayers.tsx index 511fb1a03..c56d5e6f1 100644 --- a/src/main/VideoPlayers.tsx +++ b/src/main/VideoPlayers.tsx @@ -24,8 +24,6 @@ import { selectTheme } from "../redux/themeSlice"; const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, widthInPercent = 100}) => { - const theme = useSelector(selectTheme); - const videoURLs = useSelector(selectVideoURL) const videoCount = useSelector(selectVideoCount) @@ -35,7 +33,6 @@ const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, wid justifyContent: 'center', alignItems: 'center', width: widthInPercent + '%', - boxShadow: `${theme.boxShadow_tiles}`, }); // Initialize video players @@ -48,6 +45,8 @@ const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, wid url={videoURLs[i]} isPrimary={i === 0} subtitleUrl={""} + first={i === 0} + last={i === videoCount - 1} selectIsPlaying={selectIsPlaying} selectCurrentlyAtInSeconds={selectCurrentlyAtInSeconds} selectPreviewTriggered={selectPreviewTriggered} @@ -84,6 +83,8 @@ export const VideoPlayer = React.forwardRef( url: string | undefined, isPrimary: boolean, subtitleUrl: string, + first: boolean, + last: boolean, selectIsPlaying:(state: RootState) => boolean, selectCurrentlyAtInSeconds: (state: RootState) => number, selectPreviewTriggered:(state: RootState) => boolean, @@ -103,6 +104,8 @@ export const VideoPlayer = React.forwardRef( isPrimary, selectIsPlaying, subtitleUrl, + first, + last, selectCurrentlyAtInSeconds, selectPreviewTriggered, selectClickTriggered, @@ -320,7 +323,12 @@ export const VideoPlayer = React.forwardRef( position: 'relative', width: '100%', paddingTop: `min(${aspectRatio + '%'}, ${'30vh'})`, + boxShadow: `${theme.boxShadow_tiles}`, overflow: 'hidden', + ...(first) && {borderTopLeftRadius: '5px'}, + ...(first) && {borderBottomLeftRadius: '5px'}, + ...(last) && {borderTopRightRadius: '5px'}, + ...(last) && {borderBottomRightRadius: '5px'}, }); const reactPlayerStyle = css({ From b193c39d90e36fdff3a435d928ea99d0186e42bb Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 16:53:31 +0200 Subject: [PATCH 013/107] Redesign: Fix boxshadow on videoPlayers boxshadow did not conform to the borderradius --- src/main/VideoPlayers.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/VideoPlayers.tsx b/src/main/VideoPlayers.tsx index c56d5e6f1..eaf24d870 100644 --- a/src/main/VideoPlayers.tsx +++ b/src/main/VideoPlayers.tsx @@ -24,6 +24,7 @@ import { selectTheme } from "../redux/themeSlice"; const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, widthInPercent = 100}) => { + const theme = useSelector(selectTheme) const videoURLs = useSelector(selectVideoURL) const videoCount = useSelector(selectVideoCount) @@ -33,6 +34,8 @@ const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, wid justifyContent: 'center', alignItems: 'center', width: widthInPercent + '%', + borderRadius: '5px', + boxShadow: `${theme.boxShadow_tiles}`, }); // Initialize video players @@ -323,7 +326,6 @@ export const VideoPlayer = React.forwardRef( position: 'relative', width: '100%', paddingTop: `min(${aspectRatio + '%'}, ${'30vh'})`, - boxShadow: `${theme.boxShadow_tiles}`, overflow: 'hidden', ...(first) && {borderTopLeftRadius: '5px'}, ...(first) && {borderBottomLeftRadius: '5px'}, From bb20602b5fcf09f563523e5597c49bbcc106a693 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 16:55:28 +0200 Subject: [PATCH 014/107] Redesign: Fix vertical scrollbar appearing --- src/main/Body.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/Body.tsx b/src/main/Body.tsx index 5daf030d1..760837f7a 100644 --- a/src/main/Body.tsx +++ b/src/main/Body.tsx @@ -47,7 +47,7 @@ const Body: React.FC = () => { const bodyStyle = css({ display: 'flex', flexDirection: 'row', - height: 'calc(100% - 48px)', + height: 'calc(100% - 60px)', }); return ( From 0883783d72f4e47dfcb26f329ab90a7235be8fd3 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 17:23:28 +0200 Subject: [PATCH 015/107] Redesign: Metadata Layout General Metadata Layout. Also some color changes where easy to make. More color changes to come. --- src/main/Metadata.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/Metadata.tsx b/src/main/Metadata.tsx index 1d1263d54..45676bf1f 100644 --- a/src/main/Metadata.tsx +++ b/src/main/Metadata.tsx @@ -100,17 +100,26 @@ const Metadata: React.FC = () => { display: 'grid', }) + const catalogStyle = css({ + background: `${theme.menu_background}`, + borderRadius: '5px', + boxShadow: `${theme.boxShadow_tiles}`, + marginTop: '24px', + boxSizing: "border-box", + padding: '10px', + }) + const fieldStyle = css({ display: 'flex', - flexFlow: 'row nowrap', + flexFlow: 'column nowrap', lineHeight: '2em', margin: '10px', }) const fieldLabelStyle = css({ - alignSelf: 'center', width: '110px', fontSize: '1em', + fontWeight: 'bold', lineHeight: '32px', }) @@ -118,11 +127,10 @@ const Metadata: React.FC = () => { return css({ flex: '1', fontSize: '1em', - marginLeft: '15px', borderRadius: '5px', boxShadow: isReadOnly ? '0 0 0px rgba(0, 0, 0, 0.3)' : '0 0 1px rgba(0, 0, 0, 0.3)', - ...(isReadOnly && {color: `${theme.text}`}), - color: `${theme.text}`, + ...(isReadOnly && {color: `${theme.text_black}`}), + color: `${theme.text_black}`, outline: isReadOnly ? '0px solid transparent' : `${theme.element_outline}` }); } @@ -645,7 +653,7 @@ const Metadata: React.FC = () => { return ( -
+
{i18n.exists(`metadata.${catalog.title.replaceAll(".", "-")}`) ? t(`metadata.${catalog.title.replaceAll(".", "-")}` as TFuncKey) as string : catalog.title From 2d84a8f7eb12e4aeef886c300d948f5188835a12 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 15 Jun 2023 17:37:17 +0200 Subject: [PATCH 016/107] Redesign: Metadata colors --- src/cssStyles.tsx | 14 +++++++------- src/main/Metadata.tsx | 2 +- src/themes.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 76d808dfd..5017cf58a 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -229,29 +229,29 @@ export function selectFieldStyle(theme: Theme) { }), singleValue: (provided: any) => ({ ...provided, - color: theme.text, + color: theme.text_black, }), multiValue: (provided: any) => ({ ...provided, - color: theme.selected_text, + color: theme.text_black, background: theme.multiValue, cursor: 'default', }), multiValueLabel: (provided: any) => ({ ...provided, - color: theme.selected_text, + color: theme.text_black, }), option: (provided: any, state: any) => ({ ...provided, background: state.isFocused ? theme.focused : theme.background && state.isSelected ? theme.selected : theme.background, ...(state.isFocused && {color: theme.focus_text}), - color: state.isFocused ? theme.focus_text : theme.text - && state.isSelected ? theme.selected_text : theme.text, + color: state.isFocused ? theme.focus_text : theme.text_black + && state.isSelected ? theme.selected_text : theme.text_black, }), placeholder: (provided: any) => ({ ...provided, - color: theme.text, + color: theme.text_black, }), clearIndicator: (provided: any) => ({ ...provided, @@ -267,7 +267,7 @@ export function selectFieldStyle(theme: Theme) { }), input: (provided: any) => ({ ...provided, - color: theme.text, + color: theme.text_black, }), } } diff --git a/src/main/Metadata.tsx b/src/main/Metadata.tsx index 45676bf1f..036ff7304 100644 --- a/src/main/Metadata.tsx +++ b/src/main/Metadata.tsx @@ -159,7 +159,7 @@ const Metadata: React.FC = () => { color: `${theme.indicator_color}` }, '.MuiInput-input, button': { - color: `${theme.text}`, + color: `${theme.text_black}`, background: 'transparent !important', '&:hover': { background: 'transparent !important', diff --git a/src/themes.ts b/src/themes.ts index e0c1277be..335d7ce6e 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -29,7 +29,7 @@ export const lightMode: Theme = { menuButton_outline: '2px solid transparent', button_outline: 'none', button_color: '#edf6fe', - indicator_color: '#3d3d3d', + indicator_color: '#3e8ad8', icon_color: '#fff', waveform_filter: 'invert(44%) sepia(8%) saturate(3893%) hue-rotate(169deg) brightness(99%) contrast(90%)', // All this just to turn the black part of the waveform image blue. Generated with: https://isotropic.co/tool/hex-color-to-css-filter/ waveform_bg: '', From 4097ca1c0555bca78407fa3b44a65becb4836429 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 16 Jun 2023 09:13:09 +0200 Subject: [PATCH 017/107] Fix no scrollbars in MainContent Fixes an invalid overflow value in MainContent. Would previously only have scrollbars for the whole page, which would leave the main menu looking awkwardly cut off. Now scrolls properly. --- src/main/MainContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/MainContent.tsx b/src/main/MainContent.tsx index 991c6eddd..679c4a14f 100644 --- a/src/main/MainContent.tsx +++ b/src/main/MainContent.tsx @@ -51,7 +51,7 @@ const MainContent: React.FC = () => { paddingLeft: '20px', ...(flexGapReplacementStyle(20, false)), background: `${theme.background}`, - overflow: 'vertical', + overflow: 'auto', }) const cuttingStyle = css({ From a99b16d048574bdc6ee2afff8317082c0e534e4e Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 16 Jun 2023 11:06:28 +0200 Subject: [PATCH 018/107] Redesign: Track Selection Changes the design for the track selection --- src/i18n/locales/en-US.json | 2 +- src/main/TrackSelection.tsx | 79 +++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index fd8754ccb..ffa8c7b4a 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -218,7 +218,7 @@ }, "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", diff --git a/src/main/TrackSelection.tsx b/src/main/TrackSelection.tsx index 02a395295..1d5d6ccaa 100644 --- a/src/main/TrackSelection.tsx +++ b/src/main/TrackSelection.tsx @@ -2,14 +2,13 @@ import React from "react"; import { css } from '@emotion/react' import { IconType } from "react-icons"; -import { FiInfo } from "react-icons/fi"; import { FaTrash, FaTrashRestore } from "react-icons/fa"; import ReactPlayer from 'react-player' import { Track } from '../types' import { useSelector, useDispatch } from 'react-redux'; import { selectVideos, setTrackEnabled } from '../redux/videoSlice' -import { basicButtonStyle, deactivatedButtonStyle } from '../cssStyles' +import { basicButtonStyle, deactivatedButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold } from '../cssStyles' import { useTranslation } from 'react-i18next'; import { selectTheme } from "../redux/themeSlice"; @@ -27,34 +26,48 @@ const TrackSelection: React.FC = () => { ); + const trackSelectionStyle = css({ + display: 'flex', + width: 'auto', + height: '100%', + flexDirection: 'column', + // justifyContent: 'center', + alignItems: 'center', + }) + + const trackAreaStyle = css({ + display: 'flex', + width: '100%', + height: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + alignItems: 'center', + flexWrap: 'wrap', + ...(flexGapReplacementStyle(40, false)), + }) + return ( -
- { trackItems } - +
+
+
+ { trackItems } +
); } -const Description: React.FC = () => { +const Header: React.FC = () => { const { t } = useTranslation(); + const theme = useSelector(selectTheme) - const description: string = t('trackSelection.description', - 'Select or deselect which tracks are used for processing and publication.'); - - const descriptionStyle = css({ - display: 'flex', - alignItems: 'center', - margin: '20px', - padding: '10px', - }); + const description: string = t('trackSelection.title'); return ( - +
); } @@ -71,27 +84,25 @@ const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabl const trackItemStyle = css({ display: 'flex', - flexWrap: 'wrap', - justifyContent: 'center', - alignItems: 'center', - margin: '20px', - paddingBottom: '10px', - verticalAlign: 'middle', + flexDirection: 'column', + alignItems: 'left', + + background: `${theme.menu_background}`, + borderRadius: '7px', + boxShadow: `${theme.boxShadow_tiles}`, + boxSizing: "border-box", + padding: '20px', + ...(flexGapReplacementStyle(25, false)), }); const playerStyle = css({ display: 'inline-block', - width: '80%', - maxHeight: '200px', - margin: '10px', }); const headerStyle = css({ display: 'inline-block', width: '100%', fontWeight: 'bold', - padding: '5px 25px', - borderBottom: `${theme.menuBorder}`, textTransform: 'capitalize', fontSize: 'larger', }); @@ -124,8 +135,8 @@ const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabl return (
{ header }
-
- +
+
= ({handler, text, Icon, to const buttonStyle = [ active ? basicButtonStyle(theme) : deactivatedButtonStyle, { - margin: '10px 15px', - padding: '16px', + padding: '10px 5px', width: '25%', - boxShadow: `${theme.boxShadow}`, + boxShadow: '', + border: `1px solid ${theme.text}`, background: `${theme.element_bg}`, }]; const clickHandler = () => { From c0c54441800eea94d7a84d2e3a43ae237bb92cdc Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 16 Jun 2023 11:12:07 +0200 Subject: [PATCH 019/107] Redesign: Center track selection --- src/main/TrackSelection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/TrackSelection.tsx b/src/main/TrackSelection.tsx index 1d5d6ccaa..c24031468 100644 --- a/src/main/TrackSelection.tsx +++ b/src/main/TrackSelection.tsx @@ -40,7 +40,7 @@ const TrackSelection: React.FC = () => { width: '100%', height: '100%', flexDirection: 'row', - justifyContent: 'space-around', + justifyContent: 'center', alignItems: 'center', flexWrap: 'wrap', ...(flexGapReplacementStyle(40, false)), From 5494c0e2410f85c5c64dc80bf6214dccada326fb Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 16 Jun 2023 11:34:20 +0200 Subject: [PATCH 020/107] Redesign: Finish Menu General view and feel of the finish menu. No modals yet. --- src/cssStyles.tsx | 6 +++--- src/main/FinishMenu.tsx | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 5017cf58a..85e58c536 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -138,13 +138,13 @@ export const backOrContinueStyle = css(({ * CSS for big buttons in a dynamic grid */ export const tileButtonStyle = (theme: Theme) => css({ - width: '250px', + width: '290px', height: '220px', display: 'flex', flexDirection: 'column' as const, - fontSize: "x-large", + fontSize: "large", ...(flexGapReplacementStyle(30, false)), - boxShadow: `${theme.boxShadow}`, + boxShadow: `${theme.boxShadow_tiles}`, background: `${theme.element_bg}`, placeSelf: 'center', }); diff --git a/src/main/FinishMenu.tsx b/src/main/FinishMenu.tsx index 3d1e1695e..a5a7aa3f7 100644 --- a/src/main/FinishMenu.tsx +++ b/src/main/FinishMenu.tsx @@ -19,8 +19,8 @@ const FinishMenu : React.FC = () => { const finishMenuStyle = css({ display: 'flex', - flexDirection: 'row' as const, - justifyContent: 'space-around', + flexDirection: 'row', + justifyContent: 'center', flexWrap: 'wrap', ...(flexGapReplacementStyle(30, false)), }) @@ -64,6 +64,17 @@ const FinishMenuButton: React.FC<{Icon: IconType, stateName: finish["value"]}> = break; } + const iconStyle = css({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + + background: `${theme.button_color}`, + borderRadius: '50%', + width: '90px', + height: '90px', + }) + return (
= onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { finish() } }}> - +
+ +
{buttonString}
); From a9c32a5ad851ca8fe5206d1515ac2165822a1eb6 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 16 Jun 2023 13:03:07 +0200 Subject: [PATCH 021/107] Redesign: Settings page Implements the redesign for the shortcut page, but applies it to the current settings page. Shortcuts may be split from settings in the future though. --- src/main/KeyboardControls.tsx | 64 +++++++++++++++++++++-------------- src/main/ThemeSwitcher.tsx | 27 ++++++++------- src/themes.ts | 4 +-- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/src/main/KeyboardControls.tsx b/src/main/KeyboardControls.tsx index 1399a819e..c91f0fb59 100644 --- a/src/main/KeyboardControls.tsx +++ b/src/main/KeyboardControls.tsx @@ -8,6 +8,7 @@ import { useSelector } from "react-redux"; import { flexGapReplacementStyle } from "../cssStyles"; import { getAllHotkeys } from "../globalKeys"; import { selectTheme } from "../redux/themeSlice"; +import { titleStyle, titleStyleBold } from '../cssStyles' const Group: React.FC<{name: TFuncKey, entries: KeyMapDisplayOptions[]}> = ({name, entries}) => { @@ -19,10 +20,15 @@ const Group: React.FC<{name: TFuncKey, entries: KeyMapDisplayOptions[]}> = ({nam flexDirection: 'column' as const, width: '460px', maxWidth: '50vw', + + background: `${theme.menu_background}`, + borderRadius: '5px', + boxShadow: `${theme.boxShadow_tiles}`, + boxSizing: "border-box", + padding: '0px 20px 20px 20px', }); const headingStyle = css({ - borderBottom: `${theme.menuBorder}` }) return ( @@ -42,60 +48,65 @@ const Entry: React.FC<{params: KeyMapDisplayOptions}> = ({params}) => { const entryStyle = css({ display: 'flex', - flexDirection: 'row' as const, + flexFlow: 'column nowrap', + justifyContent: 'left', width: '100%', - paddingBottom: '5px', - paddingTop: '5px', + padding: '10px 0px', + ...(flexGapReplacementStyle(10, true)) }); const labelStyle = css({ - alignSelf: 'center', - minWidth: '130px', - height: '5em', + fontWeight: 'bold', overflow: 'hidden', textOverflow: 'ellipsis', wordWrap: 'break-word', + }) - // Center text vertically + const sequencesStyle = css({ display: 'flex', - justifyContent: 'center', - alignContent: 'center', - flexDirection: 'column', + flexDirection: 'row', + ...(flexGapReplacementStyle(10, true)) }) const sequenceStyle = css({ - alignSelf: 'center', - marginLeft: '15px', display: 'flex', - flexDirection: 'row' as const, + flexDirection: 'row', ...(flexGapReplacementStyle(10, true)) }) const singleKeyStyle = css({ - borderRadius: '5px', + borderRadius: '4px', borderWidth: '2px', borderStyle: 'solid', borderColor: `${theme.singleKey_border}`, background: `${theme.singleKey_bg}`, + boxShadow: `0 2px 2px 0px rgba(150, 150, 150, 0.5)`, padding: '10px', + color: `${theme.text_black}`, }) const orStyle = css({ alignSelf: 'center', - lineHeight: '32px', + fontSize: '20px', + fontWeight: 'bold', }) return (
{params.name || t("keyboardControls.missingLabel")}
- {params.sequences.map((sequence, index, arr) => ( -
- {sequence.sequence.toString().split('+').map((singleKey, index) => ( -
{singleKey}
- ))} -
{arr.length - 1 !== index && t("keyboardControls.sequenceSeparator")}
-
- ))} +
+ {params.sequences.map((sequence, index, arr) => ( +
+ {sequence.sequence.toString().split('+').map((singleKey, index, {length}) => ( + <> +
{singleKey}
+ {length - 1 !== index ?
+
: ''} + + ))} +
{arr.length - 1 !== index && t("keyboardControls.sequenceSeparator")}
+
+ ))} +
) } @@ -104,6 +115,7 @@ const Entry: React.FC<{params: KeyMapDisplayOptions}> = ({params}) => { const KeyboardControls: React.FC = () => { const { t } = useTranslation(); + const theme = useSelector(selectTheme) const keyMap = getAllHotkeys() @@ -161,9 +173,9 @@ const KeyboardControls: React.FC = () => { return (
-

+
{t("keyboardControls.header")} -

+
{render()}
diff --git a/src/main/ThemeSwitcher.tsx b/src/main/ThemeSwitcher.tsx index bcb9fdfe3..94ba3ed05 100644 --- a/src/main/ThemeSwitcher.tsx +++ b/src/main/ThemeSwitcher.tsx @@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux" import { selectTheme, selectThemeState, toggleTheme, setState } from "../redux/themeSlice"; import Select from "react-select"; -import { selectFieldStyle } from "../cssStyles"; +import { selectFieldStyle, titleStyle, titleStyleBold } from "../cssStyles"; +import { css } from '@emotion/react' const ThemeSwitcher: React.FC = () => { @@ -47,18 +48,18 @@ const ThemeSwitcher: React.FC = () => { } } - const baseStyle = { - maxWidth: '50vw', - width: '870px', - alignSelf: 'center', - padding: '20px', - } - - const headerStyle = { + const baseStyle = css({ display: 'flex', - flexDirection: 'column' as const, + flexDirection: 'column', alignItems: 'center', - } + width: '100%', + }) + + const dropdownStyle = css({ + outline: `${theme.element_outline}`, + borderRadius: '5px', + minWidth: '50vw', + }) const themes = [ { value: 'system', label: t('theme.system') }, @@ -70,9 +71,9 @@ const ThemeSwitcher: React.FC = () => { return (
-

{t('theme.appearance')}

+
{t('theme.appearance')}
{ blurWithSubmit(e, input) }} + readOnly={field.readOnly} + css={[fieldTypeStyle(field.readOnly), inputFieldTypeStyle(field.readOnly), + { + resize: 'none', + } + ]} + data-testid="dateTimePicker" + /> + ); } else if (field.type === "time") { return ( -
- - { blurWithSubmit(e, input) }, - showError: showErrorOnBlur - }} - /> - -
+ + { blurWithSubmit(e, input) }} + readOnly={field.readOnly} + css={[fieldTypeStyle(field.readOnly), inputFieldTypeStyle(field.readOnly), + { + resize: 'none', + } + ]} + /> + ); } else if (field.type === "text_long") { return ( @@ -637,6 +592,18 @@ const Metadata: React.FC = () => { if ((field.type === "date" || field.type === "time") && input.value === "") { const {value, ...other} = input return generateComponent(field, other) + } + // is picky about its value and won't accept + // global datetime strings, so we have to convert them to local ourselves. + // TODO: Also we really should not be modifying the input element like that + // so ideally the conversion happens somewhere else in the code + // (see error in the console for further details) + if ((field.type === "date" || field.type === "time")) { + input = cloneDeep(input) + const leDate = new Date(input.value) + leDate.setMinutes(leDate.getMinutes() - leDate.getTimezoneOffset()); + input.value = leDate.toISOString().slice(0, 16); + return generateComponent(field, input) } else { return generateComponent(field, input) } From 50c40edb934ea2f2b12e8674147cb9fb27733bb2 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 14 Jul 2023 15:15:09 +0200 Subject: [PATCH 070/107] Removes playwright test for datetime picker Removes the test for datetimepickers from playwright for now. The tests relied on the creation of certain dom elements, and this is simply not feasible with a native picker. --- tests/metadata.test.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/metadata.test.ts b/tests/metadata.test.ts index 6db9cfdd2..00d970944 100644 --- a/tests/metadata.test.ts +++ b/tests/metadata.test.ts @@ -66,21 +66,6 @@ test.describe('Test Metadata-Page', () => { // Contributor await page.click('text=Contributor(s)Select... >> svg'); await page.click('div[id*="option-15"]'); - - // startDate - // Expect date-time selector to pop up and hide again - // Headless Firefox is picked up as mobile and this test will not work. - // That's why we exclude it: - // https://github.com/microsoft/playwright/issues/7769 - if (browserName != 'firefox') { - /* eslint-disable jest/no-conditional-expect */ - await page.click('div[data-testid="startDate"] >> button'); - await expect(page.locator('div.MuiPaper-root').first()).toBeVisible(); - await page.click('div[data-testid="startDate"] >> input'); - await expect(page.locator('div.MuiPaper-root').first()).toBeHidden(); - /* eslint-enable jest/no-conditional-expect */ - } - }); }); From 822351643d882ca1e5ef2a2326dee8bc1e79f213 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 14 Jul 2023 15:28:55 +0200 Subject: [PATCH 071/107] Generalize some CSS --- src/cssStyles.tsx | 9 +++++++++ src/main/Thumbnail.tsx | 19 ++++++------------- src/main/TrackSelection.tsx | 11 ++--------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 1457db5e1..6c7c0abaa 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -451,3 +451,12 @@ export const videosStyle = (theme: Theme) => css(({ padding: '10px', ...(flexGapReplacementStyle(10, false)), })) + +export const backgroundBoxStyle = (theme: Theme) => css(({ + background: `${theme.menu_background}`, + borderRadius: '7px', + boxShadow: `${theme.boxShadow_tiles}`, + boxSizing: "border-box", + padding: '20px', + ...(flexGapReplacementStyle(25, false)), +})) diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index a7d0b286e..47732b2fa 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -5,7 +5,8 @@ import React from "react"; import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; import { settings } from "../config"; -import { basicButtonStyle, deactivatedButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold, videosStyle } from "../cssStyles"; +import { basicButtonStyle, deactivatedButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold, videosStyle, + backgroundBoxStyle } from "../cssStyles"; import { selectTheme, Theme } from "../redux/themeSlice"; import { selectOriginalThumbnails, selectVideos, selectTracks, setHasChanges, setThumbnail, setThumbnails } from "../redux/videoSlice"; import { Track } from "../types"; @@ -229,7 +230,7 @@ const ThumbnailTableRow: React.FC<{ } return ( -
+
{track.flavor.type + renderPriority(track.thumbnailPriority)}
@@ -492,7 +493,7 @@ const ThumbnailTableSingleRow: React.FC<{ const theme = useSelector(selectTheme); return ( -
+
{t("thumbnailSimple.rowTitle")}
@@ -575,18 +576,10 @@ const ThumbnailButtonsSimple : React.FC<{ /** * CSS shared between multi and simple display mode */ -const thumbnailTableRowStyle = (theme: Theme) => css(({ +const thumbnailTableRowStyle = css({ display: 'flex', flexDirection: 'column', - // padding: '6px 12px', - - background: `${theme.menu_background}`, - borderRadius: '7px', - boxShadow: `${theme.boxShadow_tiles}`, - boxSizing: "border-box", - padding: '20px', - ...(flexGapReplacementStyle(25, false)), -})) +}) const thumbnailTableRowTitleStyle = css({ textAlign: 'left', diff --git a/src/main/TrackSelection.tsx b/src/main/TrackSelection.tsx index 9d87f681d..18fbad545 100644 --- a/src/main/TrackSelection.tsx +++ b/src/main/TrackSelection.tsx @@ -9,7 +9,7 @@ import ReactPlayer from 'react-player' import { Track } from '../types' import { useSelector, useDispatch } from 'react-redux'; import { selectVideos, setTrackEnabled } from '../redux/videoSlice' -import { basicButtonStyle, customIconStyle, deactivatedButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold } from '../cssStyles' +import { backgroundBoxStyle, basicButtonStyle, customIconStyle, deactivatedButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold } from '../cssStyles' import { useTranslation } from 'react-i18next'; import { selectTheme } from "../redux/themeSlice"; @@ -87,13 +87,6 @@ const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabl display: 'flex', flexDirection: 'column', alignItems: 'left', - - background: `${theme.menu_background}`, - borderRadius: '7px', - boxShadow: `${theme.boxShadow_tiles}`, - boxSizing: "border-box", - padding: '20px', - ...(flexGapReplacementStyle(25, false)), }); const playerStyle = css({ @@ -135,7 +128,7 @@ const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabl } return ( -
+
{ header }
From 9d73c8d70295d53c22848f29453061f86a53f47c Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 17 Jul 2023 14:43:25 +0200 Subject: [PATCH 072/107] Various small DarkMode improvements KeyboardControls boxShadow Thumbnail Buttons Scrubber dragHandle --- src/main/KeyboardControls.tsx | 2 +- src/main/Thumbnail.tsx | 10 ++++++++-- src/redux/themeSlice.ts | 1 + src/themes.ts | 8 ++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/KeyboardControls.tsx b/src/main/KeyboardControls.tsx index e3e6d0123..48fd986bb 100644 --- a/src/main/KeyboardControls.tsx +++ b/src/main/KeyboardControls.tsx @@ -82,7 +82,7 @@ const Entry: React.FC<{params: KeyMapDisplayOptions}> = ({params}) => { borderStyle: 'solid', borderColor: `${theme.singleKey_border}`, background: `${theme.singleKey_bg}`, - boxShadow: `0 2px 2px 0px rgba(150, 150, 150, 0.2)`, + boxShadow: `${theme.singleKey_boxShadow}`, padding: '10px', color: `${theme.text}`, }) diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index 47732b2fa..58f8cd9df 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -330,6 +330,11 @@ const ThumbnailButtons : React.FC<{ dispatch(setHasChanges(true)) } + const verticalLineStyle = css({ + borderTop: '1px solid #DDD;', + width: '100%', + }) + return (
+
{ upload(index) }} text={t('thumbnail.buttonUpload')} @@ -359,6 +365,7 @@ const ThumbnailButtons : React.FC<{ onChange={event => uploadCallback(event, track)} aria-hidden="true" /> +
{ setForOtherThumbnails(track.thumbnailUri) }} text={t('thumbnail.buttonUseForOtherThumbnails')} @@ -367,6 +374,7 @@ const ThumbnailButtons : React.FC<{ Icon={FiCopy} active={(track.thumbnailUri && track.thumbnailUri.startsWith("data") ? true : false)} /> +
{ discard(track.id) }} text={t('thumbnail.buttonDiscard')} @@ -605,7 +613,6 @@ const thumbnailButtonsStyle = css({ }, display: 'flex', flexDirection: 'column', - ...(flexGapReplacementStyle(20, true)), }) const thumbnailButtonStyle = (active: boolean, theme: Theme) => [ @@ -613,7 +620,6 @@ const thumbnailButtonStyle = (active: boolean, theme: Theme) => [ { width: '100%', height: '100%', - boxShadow: `${theme.boxShadow}`, background: `${theme.element_bg}`, justifySelf: 'center', alignSelf: 'center', diff --git a/src/redux/themeSlice.ts b/src/redux/themeSlice.ts index 45ba241ea..be9140a45 100644 --- a/src/redux/themeSlice.ts +++ b/src/redux/themeSlice.ts @@ -17,6 +17,7 @@ export interface Theme { boxShadow_tiles: string singleKey_bg: string singleKey_border: string + singleKey_boxShadow: string invert_wave: string inverted_text: string tooltip: string diff --git a/src/themes.ts b/src/themes.ts index 384851b44..4be482686 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -19,6 +19,7 @@ export const lightMode: Theme = { boxShadow_tiles: '0 5px 10px 0px rgba(150, 150, 150, 0.5)', singleKey_bg: COLORS.neutral10, singleKey_border: COLORS.neutral20, + singleKey_boxShadow: `0 2px 2px 0px rgba(150, 150, 150, 0.2)`, invert_wave: 'invert(0%)', inverted_text: COLORS.neutral90, // for "Generating waveform" text: in lightMode it's not inverted, so it has to be black tooltip: COLORS.neutral80, @@ -69,6 +70,7 @@ export const darkMode: Theme = { boxShadow_tiles: '0 5px 10px 0px rgba(0, 0, 0, 0.3)', singleKey_bg: 'linear-gradient(180deg, rgba(40,40,40,1) 0%, rgba(30,30,30,1) 100%)', singleKey_border: COLORS.neutral20, + singleKey_boxShadow: `0 2px 2px 0px rgba(0, 0, 0, 1.0)`, invert_wave: 'invert(100%)', inverted_text: COLORS.neutral90, tooltip: COLORS.neutral80, @@ -87,8 +89,8 @@ export const darkMode: Theme = { waveform_filter: 'invert(11%)', waveform_bg: '#fff', scrubber: COLORS.neutral60, - scrubber_handle: COLORS.neutral05, - scrubber_icon: COLORS.neutral60, + scrubber_handle: COLORS.neutral70, + scrubber_icon: COLORS.neutral20, subtitle_segment_bg: 'rgba(0, 0, 0, 0.4)', subtitle_segment_border: `1px solid ${COLORS.neutral80}`, subtitle_segment_text: COLORS.neutral90, @@ -119,6 +121,7 @@ export const highContrastDarkMode: Theme = { boxShadow_tiles: '0 0 0 rgba(255, 255, 255, 0.3)', singleKey_bg: 'none', singleKey_border: '#fff', + singleKey_boxShadow: `0 2px 2px 0px rgba(150, 150, 150, 0.2)`, invert_wave: 'invert(100%)', inverted_text: '#000', tooltip: '#fff', @@ -169,6 +172,7 @@ export const highContrastLightMode: Theme = { boxShadow_tiles: '0 0 0 rgba(0, 0, 0, 0.3)', singleKey_bg: 'none', singleKey_border: '#000', + singleKey_boxShadow: `0 2px 2px 0px rgba(150, 150, 150, 0.2)`, invert_wave: 'invert(0%)', inverted_text: '#fff', tooltip: '#000', From c869b41dcf8f85efe923da63a09b2fcb05b4d75a Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 17 Jul 2023 16:50:00 +0200 Subject: [PATCH 073/107] Fix Main Menu Overflow The main menu would overflow due to an illegal overflow value. This sets a legal overflow value, allowing the main menu to fill the full height and scroll on its own when necessary. --- src/main/MainMenu.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/MainMenu.tsx b/src/main/MainMenu.tsx index 80421d9fa..d92089b2b 100644 --- a/src/main/MainMenu.tsx +++ b/src/main/MainMenu.tsx @@ -39,7 +39,8 @@ const MainMenu: React.FC = () => { flexDirection: 'column', alignItems: 'center', padding: '20px', - overflow: 'vertical', + overflowX: 'hidden', + overflowY: 'auto', background: `${theme.menu_background}`, ...(flexGapReplacementStyle(30, false)), }); From 09dd6ab70ca1b6f3ada53b3d90e8bb963ec9f4ec Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 20 Jul 2023 12:34:24 +0200 Subject: [PATCH 074/107] Background color matching with Studio Some more general color changes to align colors with Studio. --- src/main/Header.tsx | 1 + src/themes.ts | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/Header.tsx b/src/main/Header.tsx index 050690746..4ab220bb8 100644 --- a/src/main/Header.tsx +++ b/src/main/Header.tsx @@ -35,6 +35,7 @@ function Header() { const logo = css({ marginLeft: '6px', + opacity: '0.8', // Unset a bunch of CSS to keep the logo clean outline: 'unset', diff --git a/src/themes.ts b/src/themes.ts index 4be482686..4619563c2 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -4,7 +4,7 @@ import { Theme } from "./redux/themeSlice"; import { COLORS } from "./util/appkit"; export const lightMode: Theme = { - background: COLORS.neutral15, + background: COLORS.neutral10, menu_background: COLORS.neutral05, text: COLORS.neutral90, error: '#ed1741', @@ -43,7 +43,7 @@ export const lightMode: Theme = { subtitle_segment_bg: 'rgba(0, 0, 0, 0.4)', subtitle_segment_border: `1px solid ${COLORS.neutral80}`, subtitle_segment_text: COLORS.neutral05, - header_bg: COLORS.neutral80, + header_bg: COLORS.neutral60, metadata_highlight: COLORS.neutral50, clock_bg: COLORS.neutral15, clock_border: '2px solid transparent', @@ -55,7 +55,7 @@ export const lightMode: Theme = { }; export const darkMode: Theme = { - background: COLORS.neutral15, + background: COLORS.neutral10, menu_background: COLORS.neutral05, text: COLORS.neutral90, error: 'rgba(237, 23, 65, 0.8)', @@ -94,7 +94,7 @@ export const darkMode: Theme = { subtitle_segment_bg: 'rgba(0, 0, 0, 0.4)', subtitle_segment_border: `1px solid ${COLORS.neutral80}`, subtitle_segment_text: COLORS.neutral90, - header_bg: COLORS.neutral30, + header_bg: COLORS.neutral20, metadata_highlight: COLORS.neutral50, clock_bg: COLORS.neutral15, clock_border: '2px solid transparent', From 92d28406849adb34a0cf01e02c7ad21ccf33b73e Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 20 Jul 2023 12:46:56 +0200 Subject: [PATCH 075/107] Remove visual box from Thumbnail view Removes the box around the whole video area in the Thumbnail view. Too much boxing does not look good. --- src/main/Thumbnail.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index 58f8cd9df..d6d136140 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -93,12 +93,6 @@ const Thumbnail : React.FC = () => { display: 'flex', flexDirection: 'column', alignItems: 'center', - - background: `${theme.menu_background}`, - borderRadius: '7px', - boxShadow: `${theme.boxShadow_tiles}`, - boxSizing: "border-box", - padding: '20px', }) return ( From 56fd3c6d39d62562699458456a75aa21fcd2d87f Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 20 Jul 2023 12:59:44 +0200 Subject: [PATCH 076/107] Put a white canvas around videos --- src/main/VideoPlayers.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/VideoPlayers.tsx b/src/main/VideoPlayers.tsx index e065c1c75..9e27bc9ec 100644 --- a/src/main/VideoPlayers.tsx +++ b/src/main/VideoPlayers.tsx @@ -22,6 +22,8 @@ import { ActionCreatorWithPayload } from "@reduxjs/toolkit"; import { selectTheme } from "../redux/themeSlice"; +import { backgroundBoxStyle, flexGapReplacementStyle } from '../cssStyles' + const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, widthInPercent = 100}) => { const videoURLs = useSelector(selectVideoURL) @@ -33,7 +35,7 @@ const VideoPlayers: React.FC<{refs: any, widthInPercent?: number}> = ({refs, wid justifyContent: 'center', width: widthInPercent + '%', borderRadius: '5px', - // boxShadow: `${theme.boxShadow_tiles}`, + ...(flexGapReplacementStyle(10, false)), maxHeight: '300px', }); @@ -337,7 +339,7 @@ export const VideoPlayer = React.forwardRef( if (!errorState) { return ( Date: Wed, 26 Jul 2023 16:38:19 +0200 Subject: [PATCH 077/107] Add basic language switcher component Adds a language switcher to the settings tab, in the style of the theme switcher component. This commit focuses on adding the language switching functionality. It does not yet concern with making the switchers look like in Tobira. --- src/i18n/locales/en-US.json | 4 +++ src/main/LanguageSwitcher.tsx | 61 +++++++++++++++++++++++++++++++++++ src/main/MainContent.tsx | 2 ++ 3 files changed, 67 insertions(+) create mode 100644 src/main/LanguageSwitcher.tsx diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index ae04c1707..2402a7f74 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -294,5 +294,9 @@ "system": "System design", "high-contrast-dark": "High contrast (Dark mode)", "high-contrast-light": "High contrast (Light mode)" + }, + + "language": { + "language": "Language" } } diff --git a/src/main/LanguageSwitcher.tsx b/src/main/LanguageSwitcher.tsx new file mode 100644 index 000000000..4335651f5 --- /dev/null +++ b/src/main/LanguageSwitcher.tsx @@ -0,0 +1,61 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { useSelector } from "react-redux" +import { selectTheme } from "../redux/themeSlice"; +import Select from "react-select"; +import { selectFieldStyle, titleStyle, titleStyleBold } from "../cssStyles"; +import { css } from '@emotion/react' +import i18next from "i18next"; + +const LanguageSwitcher: React.FC = () => { + + const { t } = useTranslation(); + const theme = useSelector(selectTheme); + + const changeLanguage = (lng: string | undefined) => { + i18next.changeLanguage(lng); + } + + const languageNames = (language: string) => { + return new Intl.DisplayNames([language], { + type: 'language' + }).of(language); + } + + const resourcesArray: string[] | undefined = i18next.options.resources && Object.keys(i18next.options.resources); + + const languages = resourcesArray?.map(entry => { + return { value: entry, label: languageNames(entry) } + }) + + const baseStyle = css({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + width: '100%', + }) + + const dropdownStyle = css({ + outline: `${theme.element_outline}`, + borderRadius: '5px', + minWidth: '50vw', + }) + + return ( +
+
{t('language.language')}
+ value === themeState)} - options={themes} - onChange={themes => switchTheme(themes!.value)} - aria-label={t('theme.selectThemesLabel')} - /> -
- ) -} - -export default ThemeSwitcher; diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index b67ba19c9..2640c2b7e 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -7,7 +7,7 @@ import { useDispatch, useSelector } from "react-redux"; import { settings } from "../config"; import { basicButtonStyle, deactivatedButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold, videosStyle, backgroundBoxStyle } from "../cssStyles"; -import { selectTheme, Theme } from "../redux/themeSlice"; +import { Theme, useTheme } from "../themes"; import { selectOriginalThumbnails, selectVideos, selectTracks, setHasChanges, setThumbnail, setThumbnails } from "../redux/videoSlice"; import { Track } from "../types"; import Timeline from "./Timeline"; @@ -27,7 +27,7 @@ const Thumbnail : React.FC = () => { const { t } = useTranslation() const dispatch = useDispatch() - const theme = useSelector(selectTheme); + const theme = useTheme(); const originalThumbnails = useSelector(selectOriginalThumbnails) // Generate Refs @@ -210,7 +210,7 @@ const ThumbnailTableRow: React.FC<{ }> = ({track, index, inputRefs, generate, upload, uploadCallback, discard}) => { const { t } = useTranslation() - const theme = useSelector(selectTheme); + const theme = useTheme(); const renderPriority = (thumbnailPriority: number) => { if (isNaN(thumbnailPriority)) { @@ -253,7 +253,7 @@ const ThumbnailTableRow: React.FC<{ const ThumbnailDisplayer : React.FC<{track: Track}> = ({track}) => { const { t } = useTranslation() - const theme = useSelector(selectTheme) + const theme = useTheme() const generalStyle = css({ width: '100%', @@ -387,7 +387,7 @@ const ThumbnailButton : React.FC<{ Icon: IconType, active: boolean, }> = ({handler, text, tooltipText, ariaLabel, Icon, active}) => { - const theme = useSelector(selectTheme); + const theme = useTheme(); const ref = React.useRef(null) const clickHandler = () => { @@ -426,7 +426,7 @@ const AffectAllRow : React.FC<{ }> = ({generate, tracks}) => { const { t } = useTranslation() - const theme = useSelector(selectTheme); + const theme = useTheme(); const generateAll = () => { for (let i = 0; i < tracks.length; i++) { @@ -490,7 +490,7 @@ const ThumbnailTableSingleRow: React.FC<{ discard: any, }> = ({track, index, inputRefs, generate, upload, uploadCallback, discard}) => { const { t } = useTranslation(); - const theme = useSelector(selectTheme); + const theme = useTheme(); return (
diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index f3790a986..257664ad3 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -22,7 +22,7 @@ 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 { useTheme } from "../themes"; import { ThemedTooltip } from './Tooltip'; import { spinningStyle } from '../cssStyles'; @@ -117,7 +117,7 @@ export const Scrubber: React.FC<{ const duration = useSelector(selectDuration) const activeSegmentIndex = useSelector(selectActiveSegmentIndex) // For ARIA information display const segments = useSelector(selectSegments) // For ARIA information display - const theme = useSelector(selectTheme) + const theme = useTheme() // Init state variables const [controlledPosition, setControlledPosition] = useState({ x: 0, y: 0 }); @@ -366,7 +366,7 @@ export const Waveforms: React.FC<{timelineHeight: number}> = ({timelineHeight}) const dispatch = useDispatch(); const videoURLs = useSelector(selectVideoURL) const videoURLStatus = useSelector((state: { videoState: { status: httpRequestState["status"] } }) => state.videoState.status); - const theme = useSelector(selectTheme); + const theme = useTheme(); // Update based on current fetching status const images = useSelector(selectWaveformImages) diff --git a/src/main/Tooltip.tsx b/src/main/Tooltip.tsx index c4851b990..302a172cf 100644 --- a/src/main/Tooltip.tsx +++ b/src/main/Tooltip.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import { selectTheme } from '../redux/themeSlice'; -import { useSelector } from 'react-redux'; +import { useTheme } from "../themes"; import Tooltip, { TooltipProps } from '@mui/material/Tooltip'; export const ThemedTooltip = ({ className, ...props }: TooltipProps) => { - const theme = useSelector(selectTheme); + const theme = useTheme(); const positionRef = React.useRef<{ x: number; y: number }>({ x: 0, y: 0 }); const areaRef = React.useRef(null); diff --git a/src/main/TrackSelection.tsx b/src/main/TrackSelection.tsx index 4fbd12350..558be6a8f 100644 --- a/src/main/TrackSelection.tsx +++ b/src/main/TrackSelection.tsx @@ -12,7 +12,7 @@ import { selectVideos, setTrackEnabled } from '../redux/videoSlice' import { backgroundBoxStyle, basicButtonStyle, customIconStyle, deactivatedButtonStyle, flexGapReplacementStyle, titleStyle, titleStyleBold } from '../cssStyles' import { useTranslation } from 'react-i18next'; -import { selectTheme } from "../redux/themeSlice"; +import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; /** @@ -59,7 +59,7 @@ const TrackSelection: React.FC = () => { const Header: React.FC = () => { const { t } = useTranslation(); - const theme = useSelector(selectTheme) + const theme = useTheme() const description: string = t('trackSelection.title'); @@ -73,7 +73,7 @@ const Header: React.FC = () => { const TrackItem: React.FC<{track: Track, enabledCount: number}> = ({track, enabledCount}) => { - const theme = useSelector(selectTheme); + const theme = useTheme() const { t } = useTranslation(); const dispatch = useDispatch(); @@ -158,7 +158,7 @@ interface selectButtonInterface { const SelectButton : React.FC = ({handler, text, Icon, tooltip, active}) => { - const theme = useSelector(selectTheme); + const theme = useTheme(); const buttonStyle = [ active ? basicButtonStyle(theme) : deactivatedButtonStyle, diff --git a/src/main/VideoControls.tsx b/src/main/VideoControls.tsx index 797e4c677..b25eb4b94 100644 --- a/src/main/VideoControls.tsx +++ b/src/main/VideoControls.tsx @@ -22,7 +22,7 @@ import { RootState } from "../redux/store"; import { ActionCreatorWithPayload } from "@reduxjs/toolkit"; import { ThemedTooltip } from "./Tooltip"; -import { selectTheme, Theme } from "../redux/themeSlice"; +import { Theme, useTheme } from "../themes"; /** * Contains controls for manipulating multiple video players at once @@ -42,7 +42,7 @@ const VideoControls: React.FC<{ setIsPlayPreview }) => { - const theme = useSelector(selectTheme); + const theme = useTheme(); const videoControlsRowStyle = css({ background: `${theme.background}`, @@ -108,7 +108,7 @@ const PreviewMode: React.FC<{ // Init redux variables const dispatch = useDispatch(); const isPlayPreview = useSelector(selectIsPlayPreview) - const theme = useSelector(selectTheme); + const theme = useTheme(); // Change preview mode from "on" to "off" and vice versa const switchPlayPreview = (event: KeyboardEvent | SyntheticEvent, ref: React.RefObject | undefined) => { @@ -187,7 +187,7 @@ const PlayButton: React.FC<{ // Init redux variables const dispatch = useDispatch(); const isPlaying = useSelector(selectIsPlaying) - const theme = useSelector(selectTheme); + const theme = useTheme(); // Change play mode from "on" to "off" and vice versa const switchIsPlaying = (event: KeyboardEvent | SyntheticEvent) => { @@ -249,7 +249,7 @@ const TimeDisplay: React.FC<{ // Init redux variables const currentlyAt = useSelector(selectCurrentlyAt) const duration = useSelector(selectDuration) - const theme = useSelector(selectTheme) + const theme = useTheme() const timeTextStyle = (theme: Theme) => css({ display: 'inline-block', diff --git a/src/main/VideoPlayers.tsx b/src/main/VideoPlayers.tsx index 9e27bc9ec..d0c89de45 100644 --- a/src/main/VideoPlayers.tsx +++ b/src/main/VideoPlayers.tsx @@ -20,7 +20,7 @@ import { sleep } from './../util/utilityFunctions' import { RootState } from "../redux/store"; import { ActionCreatorWithPayload } from "@reduxjs/toolkit"; -import { selectTheme } from "../redux/themeSlice"; +import { useTheme } from "../themes"; import { backgroundBoxStyle, flexGapReplacementStyle } from '../cssStyles' @@ -133,7 +133,7 @@ export const VideoPlayer = React.forwardRef( const clickTriggered = useSelector(selectClickTriggered) // eslint-disable-next-line @typescript-eslint/no-unused-vars const aspectRatio = useSelector(selectAspectRatio) - const theme = useSelector(selectTheme) + const theme = useTheme() // Init state variables const ref = useRef(null); diff --git a/src/main/WorkflowConfiguration.tsx b/src/main/WorkflowConfiguration.tsx index b6fd9317e..d4f721144 100644 --- a/src/main/WorkflowConfiguration.tsx +++ b/src/main/WorkflowConfiguration.tsx @@ -18,7 +18,7 @@ import { AppDispatch } from "../redux/store"; import { selectSubtitles } from "../redux/subtitleSlice"; import { serializeSubtitle } from "../util/utilityFunctions"; import { Flavor } from "../types"; -import { selectTheme } from "../redux/themeSlice"; +import { useTheme } from "../themes"; /** * Will eventually display settings based on the selected workflow index @@ -31,7 +31,7 @@ const WorkflowConfiguration : React.FC = () => { const postAndProcessError = useSelector(selectError) const postMetadataStatus = useSelector(selectPostStatus); const postMetadataError = useSelector(selectPostError); - const theme = useSelector(selectTheme); + const theme = useTheme(); const workflowConfigurationStyle = css({ display: 'flex', @@ -79,7 +79,7 @@ export const SaveAndProcessButton: React.FC<{text: string}> = ({text}) => { const workflowStatus = useSelector(selectStatus); const metadataStatus = useSelector(selectPostStatus); const [metadataSaveStarted, setMetadataSaveStarted] = useState(false); - const theme = useSelector(selectTheme); + const theme = useTheme(); // Let users leave the page without warning after a successful save useEffect(() => { diff --git a/src/main/WorkflowSelection.tsx b/src/main/WorkflowSelection.tsx index 2b16d82d9..b3855b064 100644 --- a/src/main/WorkflowSelection.tsx +++ b/src/main/WorkflowSelection.tsx @@ -19,7 +19,7 @@ import { EmotionJSX } from "@emotion/react/types/jsx-namespace"; import { useTranslation } from 'react-i18next'; import { Trans } from "react-i18next"; import { FormControlLabel, Radio, RadioGroup } from "@mui/material"; -import { selectTheme } from "../redux/themeSlice"; +import { useTheme } from "../themes"; /** * Allows the user to select a workflow @@ -34,7 +34,7 @@ const WorkflowSelection : React.FC = () => { const workflows = useSelector(selectWorkflows) const finishState = useSelector(selectFinishState) const pageNumber = useSelector(selectPageNumber) - const theme = useSelector(selectTheme) + const theme = useTheme() const postAndProcessWorkflowStatus = useSelector(selectStatus); const postAndProcessError = useSelector(selectError) @@ -183,7 +183,7 @@ const WorkflowButton: React.FC<{stateName: string, workflowId: string, workflowD const WorkflowSelectRadio: React.FC = props => { - const theme = useSelector(selectTheme) + const theme = useTheme() const style = css({ alignSelf: 'start', diff --git a/src/redux/store.ts b/src/redux/store.ts index d57c0af9c..64687d955 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -8,7 +8,6 @@ import endReducer from './endSlice' import metadataReducer from './metadataSlice' import subtitleReducer from './subtitleSlice' import errorReducer from './errorSlice' -import themeReducer from './themeSlice' export const store = configureStore({ reducer: { @@ -21,7 +20,6 @@ export const store = configureStore({ metadataState: metadataReducer, subtitleState: subtitleReducer, errorState: errorReducer, - themeState: themeReducer, } }) diff --git a/src/redux/themeSlice.ts b/src/redux/themeSlice.ts deleted file mode 100644 index be9140a45..000000000 --- a/src/redux/themeSlice.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { darkMode, lightMode, highContrastDarkMode, highContrastLightMode } from "../themes"; - -export interface Theme { - background: string - menu_background: string - text: string - error: string - element_bg: string - multiValue: string - focused: string - focus_text: string - selected: string - disabled: string - menuBorder: string - boxShadow: string - boxShadow_tiles: string - singleKey_bg: string - singleKey_border: string - singleKey_boxShadow: string - invert_wave: string - inverted_text: string - tooltip: string - tooltip_text: string - element_outline: string - selected_text: string - dropdown_border: string - menuButton_outline: string - button_outline: string - button_color: string - indicator_color: string - icon_color: string - background_finish_menu_icon: string - background_play_icon: string - background_preview_icon: string - waveform_filter: string - waveform_bg: string - scrubber: string - scrubber_handle: string - scrubber_icon: string - subtitle_segment_bg: string - subtitle_segment_border: string - subtitle_segment_text: string - header_bg: string - metadata_highlight: string - clock_bg: string - clock_border: string - clock_hands: string - clock_focus: string - digit_selected: string - text_shadow: string -} - -const getValue = () => { - const value = localStorage.getItem('theme'); - - if (value === 'system' || value === null) { - return 'system' - } - else if (value === 'high-contrast-dark') { - return 'high-contrast-dark' - } - else if (value === 'high-contrast-light') { - return 'high-contrast-light' - } - else if (value === 'dark') { - return 'dark' - } - return 'light' -}; - -const getTheme = () => { - const themeId = getValue(); - document.documentElement.setAttribute('data-theme', 'light'); - - if (themeId === 'system' || themeId === undefined) { - const isDarkPrefered = window.matchMedia('(prefers-color-scheme: dark)'); - const isContrastPrefered = window.matchMedia('(prefers-contrast: more)'); - console.log('isContrastPrefered', isContrastPrefered) - - if (isDarkPrefered.matches && !isContrastPrefered.matches) { - document.documentElement.setAttribute('data-theme', 'dark'); - return darkMode - } - else if (isContrastPrefered.matches && isDarkPrefered.matches) { - document.documentElement.setAttribute('data-theme', 'high-contrast-dark'); - return highContrastDarkMode - } - else if (isContrastPrefered.matches && !isDarkPrefered.matches) { - document.documentElement.setAttribute('data-theme', 'high-contrast-light'); - return highContrastLightMode - } - return lightMode - } - - if (themeId === 'high-contrast-dark') { - document.documentElement.setAttribute('data-theme', 'high-contrast-dark'); - return highContrastDarkMode - } - if (themeId === 'high-contrast-light') { - document.documentElement.setAttribute('data-theme', 'high-contrast-light'); - return highContrastLightMode - } - if (themeId === 'dark') { - document.documentElement.setAttribute('data-theme', 'dark'); - return darkMode - } - return lightMode -} - -export interface theme { - value: 'dark' | 'light' | 'system' | 'high-contrast-dark' | 'high-contrast-light', - theme: Theme -} - -const initialState: theme = { - value: getValue(), - theme: getTheme(), -} - -export const themeSlice = createSlice({ - name: 'themeState', - initialState, - reducers: { - setState: (state, action: PayloadAction) => { - state.value = action.payload; - }, - toggleTheme: state => { - state.theme = getTheme() - }, - } -}) - -// Export Actions -export const { setState, toggleTheme } = themeSlice.actions - -// Export Selectors -export const selectThemeState = (state: { themeState: { value: theme["value"]; }; }) => state.themeState.value -export const selectTheme = (state: { themeState: { theme: theme["theme"]; }; }) => state.themeState.theme - -export default themeSlice.reducer diff --git a/src/themes.ts b/src/themes.ts index 4619563c2..56810a2d7 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -1,8 +1,69 @@ /* eslint-disable camelcase */ -import { Theme } from "./redux/themeSlice"; +import { match, useColorScheme } from "@opencast/appkit"; import { COLORS } from "./util/appkit"; +export const useTheme = () : Theme => { + const scheme = useColorScheme() + return match(scheme.scheme, { + "light": () => lightMode, + "dark": () => darkMode, + "light-high-contrast": () => highContrastLightMode, + "dark-high-contrast": () => highContrastDarkMode, + }) +} + +export interface Theme { + background: string + menu_background: string + text: string + error: string + element_bg: string + multiValue: string + focused: string + focus_text: string + selected: string + disabled: string + menuBorder: string + boxShadow: string + boxShadow_tiles: string + singleKey_bg: string + singleKey_border: string + singleKey_boxShadow: string + invert_wave: string + inverted_text: string + tooltip: string + tooltip_text: string + element_outline: string + selected_text: string + dropdown_border: string + menuButton_outline: string + button_outline: string + button_color: string + indicator_color: string + icon_color: string + background_finish_menu_icon: string + background_play_icon: string + background_preview_icon: string + waveform_filter: string + waveform_bg: string + scrubber: string + scrubber_handle: string + scrubber_icon: string + subtitle_segment_bg: string + subtitle_segment_border: string + subtitle_segment_text: string + header_bg: string + header_button_hover_bg: string + metadata_highlight: string + clock_bg: string + clock_border: string + clock_hands: string + clock_focus: string + digit_selected: string + text_shadow: string +} + export const lightMode: Theme = { background: COLORS.neutral10, menu_background: COLORS.neutral05, @@ -44,6 +105,7 @@ export const lightMode: Theme = { subtitle_segment_border: `1px solid ${COLORS.neutral80}`, subtitle_segment_text: COLORS.neutral05, header_bg: COLORS.neutral60, + header_button_hover_bg: COLORS.neutral70, metadata_highlight: COLORS.neutral50, clock_bg: COLORS.neutral15, clock_border: '2px solid transparent', @@ -95,6 +157,7 @@ export const darkMode: Theme = { subtitle_segment_border: `1px solid ${COLORS.neutral80}`, subtitle_segment_text: COLORS.neutral90, header_bg: COLORS.neutral20, + header_button_hover_bg: COLORS.neutral10, metadata_highlight: COLORS.neutral50, clock_bg: COLORS.neutral15, clock_border: '2px solid transparent', @@ -146,6 +209,7 @@ export const highContrastDarkMode: Theme = { subtitle_segment_border: '2px solid #fff', subtitle_segment_text: '#fff', header_bg: '#000', + header_button_hover_bg: '#000', metadata_highlight: 'rgb(38, 132, 255)', clock_bg: '#000', clock_border: '2px solid #a6ffea', @@ -197,6 +261,7 @@ export const highContrastLightMode: Theme = { subtitle_segment_border: '2px solid #000', subtitle_segment_text: '#000', header_bg: '#000', + header_button_hover_bg: '#000', metadata_highlight: 'rgb(38, 132, 255)', clock_bg: 'snow', clock_border: '2px solid #000099', From f9db316b857b7a0592b030a5e3be202989135ce2 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 24 Aug 2023 16:38:52 +0200 Subject: [PATCH 085/107] Move language selector to header To get more in line with especially Studio redesign, this moves the language selector from the settings page to the header and styles it accordingly --- src/main/Header.tsx | 54 ++++++++++++++++++++++++++++++- src/main/LanguageSwitcher.tsx | 60 ----------------------------------- src/main/MainContent.tsx | 2 -- 3 files changed, 53 insertions(+), 63 deletions(-) delete mode 100644 src/main/LanguageSwitcher.tsx diff --git a/src/main/Header.tsx b/src/main/Header.tsx index 49088f2d6..4711f5f1e 100644 --- a/src/main/Header.tsx +++ b/src/main/Header.tsx @@ -6,13 +6,15 @@ import { css } from '@emotion/react' import { useTranslation } from "react-i18next"; import { MainMenuButton } from "./MainMenu"; import { FiMoon, FiSettings, FiSun } from "react-icons/fi"; +import { HiOutlineTranslate } from "react-icons/hi"; import { MainMenuStateNames } from "../types"; -import { basicButtonStyle, BREAKPOINT_MEDIUM, flexGapReplacementStyle } from '../cssStyles' +import { basicButtonStyle, BREAKPOINT_MEDIUM, BREAKPOINT_SMALL, flexGapReplacementStyle } from '../cssStyles' import { ReactComponent as LogoSvg } from '../img/opencast-editor.svg'; import { selectIsEnd } from "../redux/endSlice"; import { checkboxMenuItem, HeaderMenuItemDef, ProtoButton, useColorScheme, WithHeaderMenu } from "@opencast/appkit"; import { IconType } from "react-icons"; +import i18next from "i18next"; function Header() { const theme = useTheme() @@ -68,6 +70,7 @@ function Header() {
+ { !isEnd && { ) } +const LanguageButton: React.FC = () => { + const { t } = useTranslation(); + + const isCurrentLanguage = (language: string) => language === i18next.resolvedLanguage; + + const changeLanguage = (lng: string | undefined) => { + i18next.changeLanguage(lng); + } + + const languageNames = (language: string) => { + return new Intl.DisplayNames([language], { + type: 'language' + }).of(language); + } + + const resourcesArray: string[] | undefined = i18next.options.resources && Object.keys(i18next.options.resources); + + const languages = resourcesArray?.map(entry => { + return { value: entry, label: languageNames(entry) } + }) + + // menuItems can't deal with languages being undefined, so we return early + // until we reach a rerender with actual information + if (languages === undefined) { + return (<>) + } + + const menuItems = Object.values(languages).map(lng => checkboxMenuItem({ + checked: isCurrentLanguage(lng.value), + children: <>{lng.label}, + onClick: () => { + changeLanguage(lng!.value); + }, + })); + + const label = t("language.language"); + return ( + + + + ); +}; + const ThemeButton: React.FC = () => { const { t } = useTranslation() diff --git a/src/main/LanguageSwitcher.tsx b/src/main/LanguageSwitcher.tsx deleted file mode 100644 index bda6f5278..000000000 --- a/src/main/LanguageSwitcher.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { useTheme } from "../themes"; -import Select from "react-select"; -import { selectFieldStyle, titleStyle, titleStyleBold } from "../cssStyles"; -import { css } from '@emotion/react' -import i18next from "i18next"; - -const LanguageSwitcher: React.FC = () => { - - const { t } = useTranslation(); - const theme = useTheme(); - - const changeLanguage = (lng: string | undefined) => { - i18next.changeLanguage(lng); - } - - const languageNames = (language: string) => { - return new Intl.DisplayNames([language], { - type: 'language' - }).of(language); - } - - const resourcesArray: string[] | undefined = i18next.options.resources && Object.keys(i18next.options.resources); - - const languages = resourcesArray?.map(entry => { - return { value: entry, label: languageNames(entry) } - }) - - const baseStyle = css({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - width: '100%', - }) - - const dropdownStyle = css({ - outline: `${theme.element_outline}`, - borderRadius: '5px', - minWidth: '50vw', - }) - - return ( -
-
{t('language.language')}
- - - - + From 52b0ac7386b1e2b4378b543f089ba595127fd3b8 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 12 Sep 2023 17:44:25 +0200 Subject: [PATCH 107/107] Fix language key for portugese Language key for portugese changed in Opencast, so we need to change it in our translation files as well. --- src/i18n/locales/en-US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 25ddf643e..d8a7a0d58 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -133,7 +133,7 @@ }, "language": { "LANGUAGES-SLOVENIAN": "Slovenian", - "LANGUAGES-PORTUGUESE": "Portuguese", + "LANGUAGES-PORTUGESE": "Portuguese", "LANGUAGES-ROMANSH": "Romansh", "LANGUAGES-ARABIC": "Arabic", "LANGUAGES-POLISH": "Polish",