From 64573aff8a0559ac0563d7d47ef35cfadfce72e1 Mon Sep 17 00:00:00 2001 From: Alex Christoffer Rasmussen Date: Sun, 7 Jan 2024 23:23:25 +0100 Subject: [PATCH] refactor: update lower third (#665) * init * auto in out * fix in out animation and other options * move some constants to css override * dont show block if text source is empty * select trigger * adjust the animation * remove the old one and replace it in the router * return null from useEffect when there is no timer to clear * typo: manuel -> manual * fix: delay is excluding transition in time * rename to top and bottom * change font size to px and increese the default * sharper animation timning * fix: dont need state for options * fix: e2e test for lower third * deafault to ffffff00 key color * typo: scss naming * style: all white default bg key * style: add default values to src options * style: `white-space: nowrap;` * style: start out animation a bit early * style: browser tab title * fix: checking the correct default values in ViewParamsEditor * fix: defaultValues save improved by @asharonbaltazar * lint: capitalize enum names * fix: memoize options * fix: searchParams test * fix: readability of `topText` and `bottomText` and memoize them * fix: enum naming upper case * refactor: remove cx import * refactor: try to simplify is else flow --- apps/client/src/AppRouter.tsx | 2 +- .../view-params-editor/ViewParamsEditor.tsx | 8 +- .../view-params-editor/constants.ts | 128 +++++++-- .../viewers/lower-thirds/LowerLines.scss | 67 ----- .../viewers/lower-thirds/LowerLines.tsx | 180 ------------- .../viewers/lower-thirds/LowerThird.scss | 139 ++++++++++ .../viewers/lower-thirds/LowerThird.tsx | 243 ++++++++++++++++++ .../viewers/lower-thirds/LowerWrapper.tsx | 137 ---------- apps/server/src/external/styles/override.css | 8 + .../features/201-message-control.spec.ts | 2 +- 10 files changed, 498 insertions(+), 416 deletions(-) delete mode 100644 apps/client/src/features/viewers/lower-thirds/LowerLines.scss delete mode 100644 apps/client/src/features/viewers/lower-thirds/LowerLines.tsx create mode 100644 apps/client/src/features/viewers/lower-thirds/LowerThird.scss create mode 100644 apps/client/src/features/viewers/lower-thirds/LowerThird.tsx delete mode 100644 apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx diff --git a/apps/client/src/AppRouter.tsx b/apps/client/src/AppRouter.tsx index d5aef9b5b..05650c949 100644 --- a/apps/client/src/AppRouter.tsx +++ b/apps/client/src/AppRouter.tsx @@ -15,7 +15,7 @@ const Countdown = lazy(() => import('./features/viewers/countdown/Countdown')); const Backstage = lazy(() => import('./features/viewers/backstage/Backstage')); const Public = lazy(() => import('./features/viewers/public/Public')); -const Lower = lazy(() => import('./features/viewers/lower-thirds/LowerWrapper')); +const Lower = lazy(() => import('./features/viewers/lower-thirds/LowerThird')); const StudioClock = lazy(() => import('./features/viewers/studio/StudioClock')); const STimer = withAlias(withData(TimerView)); diff --git a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx index 55eb30f88..960194f33 100644 --- a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx +++ b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx @@ -23,14 +23,14 @@ type ViewParamsObj = { [key: string]: string | FormDataEntryValue }; type SavedViewParams = Record; const getURLSearchParamsFromObj = (paramsObj: ViewParamsObj, paramFields: ParamField[]) => { - const defaultValues = paramFields.map(({ defaultValue }) => String(defaultValue)); - + const defaultValues = paramFields.reduce>((acc, { id, defaultValue }) => { + return { ...acc, [id]: String(defaultValue) }; + }, {}); return Object.entries(paramsObj).reduce((newSearchParams, [id, value]) => { if (typeof value === 'string' && value.length) { - if (defaultValues.includes(value)) { + if (defaultValues[id] === value) { return newSearchParams; } - newSearchParams.set(id, value); return newSearchParams; diff --git a/apps/client/src/common/components/view-params-editor/constants.ts b/apps/client/src/common/components/view-params-editor/constants.ts index b8d5967c0..8150de6f5 100644 --- a/apps/client/src/common/components/view-params-editor/constants.ts +++ b/apps/client/src/common/components/view-params-editor/constants.ts @@ -233,13 +233,97 @@ export const MINIMAL_TIMER_OPTIONS: ParamField[] = [ }, ]; -export const LOWER_THIRDS_OPTIONS: ParamField[] = [ +export const LOWER_THIRD_OPTIONS: ParamField[] = [ { - id: 'size', - title: 'Size', - description: 'Scales the current style (0.5 = 50% 1 = 100% 2 = 200%)', + id: 'trigger', + title: 'Animation Trigger', + description: '', + type: 'option', + values: { + event: 'Event Load', + manual: 'Manual', + }, + defaultValue: 'event', + }, + { + id: 'top-src', + title: 'Top Text', + description: '', + type: 'option', + values: { + title: 'Title', + subtitle: 'Subtitle', + presenter: 'Presenter', + lowerMsg: 'Lower Thrid Message', + }, + defaultValue: 'title', + }, + { + id: 'bottom-src', + title: 'Bottom Text', + description: 'Select the text source for the bottom element', + type: 'option', + values: { + title: 'Title', + subtitle: 'Subtitle', + presenter: 'Presenter', + lowerMsg: 'Lower Thrid Message', + }, + defaultValue: 'subtitle', + }, + { + id: 'top-colour', + title: 'Top Text Colour', + description: 'Top text colour in hexadecimal', + prefix: '#', + type: 'string', + placeholder: '0000ff (default)', + }, + { + id: 'bottom-colour', + title: 'Bottom Text Colour', + description: 'Bottom text colour in hexadecimal', + prefix: '#', + type: 'string', + placeholder: '0000ff (default)', + }, + { + id: 'top-bg', + title: 'Top Background Colour', + description: 'Top text background colour in hexadecimal', + prefix: '#', + type: 'string', + placeholder: '00000000 (default)', + }, + { + id: 'bottom-bg', + title: 'Bottom Background Colour', + description: 'Bottom text background colour in hexadecimal', + prefix: '#', + type: 'string', + placeholder: '00000000 (default)', + }, + { + id: 'top-size', + title: 'Top Text Size', + description: 'Font size of the top text', + type: 'string', + placeholder: '65px', + }, + { + id: 'bottom-size', + title: 'Bottom Text Size', + description: 'Font size of the bottom text', + type: 'string', + placeholder: '64px', + }, + { + id: 'width', + title: 'Minimum Width', + description: 'Minimum Width of the element', type: 'number', - placeholder: '1 (default)', + prefix: '%', + placeholder: '45 (default)', }, { id: 'transition', @@ -249,35 +333,27 @@ export const LOWER_THIRDS_OPTIONS: ParamField[] = [ placeholder: '3 (default)', }, { - id: 'text', - title: 'Text Colour', - description: 'Text colour in hexadecimal', - prefix: '#', - type: 'string', - placeholder: 'fffffa (default)', - }, - { - id: 'bg', - title: 'Text Background', - description: 'Text background colour in hexadecimal', - prefix: '#', - type: 'string', - placeholder: '00000033 (default)', + id: 'delay', + title: 'Delay', + description: 'Delay between transition in and out in seconds (default 3)', + type: 'number', + placeholder: '3 (default)', }, { id: 'key', title: 'Key Colour', - description: 'Screen background colour in hexadecimal', + description: 'Colour of the background', prefix: '#', type: 'string', - placeholder: '00000033 (default)', + placeholder: 'ffffffff (default)', }, { - id: 'fadeout', - title: 'Fadeout', - description: 'Time (in seconds) the lower third displays before fading out', - type: 'number', - placeholder: '3 (default)', + id: 'line-colour', + title: 'Line Colour', + description: 'Colour of the line', + prefix: '#', + type: 'string', + placeholder: 'ff0000ff (default)', }, ]; diff --git a/apps/client/src/features/viewers/lower-thirds/LowerLines.scss b/apps/client/src/features/viewers/lower-thirds/LowerLines.scss deleted file mode 100644 index ceaa0459a..000000000 --- a/apps/client/src/features/viewers/lower-thirds/LowerLines.scss +++ /dev/null @@ -1,67 +0,0 @@ -.lower-third.lines { - margin: 0; - box-sizing: border-box; - overflow: hidden; - width: 100%; - height: 100%; - color: #fffffa; - font-size: 4vh; - background-color: white; - - .lower-container { - position: absolute; - top: 75vh; - background-color: #0003; - padding: 1vh 1vw 1vh 0; - display: flex; - flex-direction: column; - width: 45vw; - border-radius: 0 8px 8px 0; - } - - .message-container { - position: absolute; - bottom: 2vh; - width: 100%; - text-align: center; - background-color: #0003; - - .message { - font-size: 3.5vh; - } - } - - .title-container, - .subtitle-container { - display: grid; - grid-template-columns: auto max-content; - grid-template-areas: 'decor text'; - align-items: center; - position: relative; - margin-bottom: 1vh; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .title-decor, - .sub-decor { - grid-area: decor; - height: 4vh; - width: 100%; - background-color: #ff6969; - background: linear-gradient( - 90deg, - rgb(255 105 105 / 100%) 0%, - rgb(255 132 132 / 100%) 100% - ); - } - - .title, - .subtitle { - grid-area: text; - padding-left: 1vw; - justify-self: right; - width: fit-content; - } -} diff --git a/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx b/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx deleted file mode 100644 index af2fa8f2e..000000000 --- a/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { useEffect, useState } from 'react'; -import { AnimatePresence, motion } from 'framer-motion'; -import { Message } from 'ontime-types'; - -import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; -import { LOWER_THIRDS_OPTIONS } from '../../../common/components/view-params-editor/constants'; -import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; - -import { LowerOptions } from './LowerWrapper'; - -import './LowerLines.scss'; - -interface LowerLinesProps { - lower: Message; - heading: string; - subheading: string; - options: LowerOptions; - doShow: boolean; -} - -export default function LowerLines(props: LowerLinesProps) { - const { lower, heading, subheading, options, doShow } = props; - const [showLower, setShowLower] = useState(true); - - // Unmount if fadeOut - useEffect(() => { - if (!options.fadeOut) return; - - // Calculate time - const fadeOutTime = (options.fadeOut + options.transitionIn) * 1000; - - const timeout = setTimeout(() => { - setShowLower(false); - }, fadeOutTime); - - return () => clearTimeout(timeout); - }, [options.fadeOut, options.transitionIn]); - - useEffect(() => { - setShowLower(doShow); - }, [doShow]); - - // Format messages - const showLowerMessage = lower.text !== '' && lower.visible; - - // motion - // transition segments - const t = options.transitionIn; - const eight = t / 8; - const quarter = t / 4; - const third = t / 3; - const half = t / 2; - - const lowerThirdVariants = { - hidden: { - opacity: 0, - }, - visible: { - opacity: 1, - transition: { - duration: third, - }, - }, - exit: { - opacity: 0, - x: -1000, - transition: { - duration: third, - }, - }, - }; - - const titleContainerVariants = { - hidden: { - left: -1000, - }, - visible: { - left: 0, - transition: { - duration: third, - }, - }, - }; - - const titleVariants = { - hidden: { - opacity: 0, - }, - visible: { - opacity: 1, - transition: { - delay: quarter, - duration: half, - }, - }, - }; - - const subtitleContainerVariants = { - hidden: { - left: -1000, - }, - visible: { - left: 0, - transition: { - delay: eight, - duration: third, - }, - }, - }; - - const subtitleVariants = { - hidden: { - opacity: 0, - }, - visible: { - opacity: 1, - transition: { - delay: third, - duration: third * 2, - }, - }, - }; - - const sizeMultiplier = (options.size || 1) * 4; - - return ( -
- - - - - {showLower && ( - - - - {heading} - -
- - -
- - {subheading} - - - - )} - - - - {showLowerMessage && ( - -
{lower.text}
-
- )} -
-
- ); -} diff --git a/apps/client/src/features/viewers/lower-thirds/LowerThird.scss b/apps/client/src/features/viewers/lower-thirds/LowerThird.scss new file mode 100644 index 000000000..a98d19f9a --- /dev/null +++ b/apps/client/src/features/viewers/lower-thirds/LowerThird.scss @@ -0,0 +1,139 @@ +.lower-third { + margin: 0; + width: 100%; + height: 100%; + + .container { + position: absolute; + display: block; + top: 75vh; + animation-fill-mode: both; + animation-timing-function: ease-out; + + &--pre { + animation-name: in; + animation-play-state: paused; + .data-top { + animation-play-state: paused; + animation-name: top-in; + } + .data-lower { + animation-play-state: paused; + animation-name: bottom-in; + } + } + &--in { + animation-name: in; + animation-play-state: running; + .data-top { + animation-play-state: running; + animation-name: top-in; + } + .data-bottom { + animation-play-state: running; + animation-name: bottom-in; + } + } + &--out { + animation-name: out; + animation-play-state: running; + .data-top { + animation-play-state: running; + animation-name: top-out; + } + .data-bottom { + animation-play-state: running; + animation-name: bottom-out; + } + } + + .line { + width: 100%; + height: var(--bottomThird-line-height-override, 0.5vh); + } + .clip { + overflow: hidden; + } + .data-bottom, + .data-top { + padding-left: 3vw; + padding-right: 3vw; + font-family: var(--lowerThird-font-family-override, 'Arial'); + text-align: var(--lowerThird-text-align-override, left); + white-space: nowrap; + } + + .data-top { + font-weight: var(--lowerThird-top-font-weight-override, bold); + font-style: var(--lowerThird-top-font-style-override, normal); + } + .data-bottom { + font-weight: var(--lowerThird-bottom-font-weight-override, normal); + font-style: var(--lowerThird-bottom-font-style-override, normal); + } + } +} + +@keyframes in { + 0% { + transform: translateX(-100%); + opacity: 0%; + } + 50%, + 100% { + transform: translateX(0%); + opacity: 100%; + } +} + +@keyframes out { + 0%, + 30% { + transform: translateX(0%); + opacity: 100%; + } + 100% { + transform: translateX(-100%); + opacity: 0%; + } +} + +@keyframes top-in { + 0%, + 50% { + transform: translateY(100%); + } + 100% { + transform: translateY(0%); + } +} + +@keyframes top-out { + 0% { + transform: translateY(0%); + } + 50%, + 100% { + transform: translateY(100%); + } +} + +@keyframes bottom-in { + 0%, + 50% { + transform: translateY(-100%); + } + 100% { + transform: translateY(0%); + } +} + +@keyframes bottom-out { + 0% { + transform: translateY(0%); + } + 50%, + 100% { + transform: translateY(-100%); + } +} diff --git a/apps/client/src/features/viewers/lower-thirds/LowerThird.tsx b/apps/client/src/features/viewers/lower-thirds/LowerThird.tsx new file mode 100644 index 000000000..56679ab76 --- /dev/null +++ b/apps/client/src/features/viewers/lower-thirds/LowerThird.tsx @@ -0,0 +1,243 @@ +import { useEffect, useMemo, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { Message, OntimeEvent, ViewSettings } from 'ontime-types'; + +import { overrideStylesURL } from '../../../common/api/apiConstants'; +import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; +import { LOWER_THIRD_OPTIONS } from '../../../common/components/view-params-editor/constants'; +import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; +import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; + +import './LowerThird.scss'; + +enum SrcKeys { + Title = 'title', + Subtitle = 'subtitle', + Presenter = 'presenter', + LowerMsg = 'lowerMsg', +} + +enum TriggerType { + Event = 'event', + Manual = 'manual', +} + +type LowerOptions = { + width: number; + trigger: TriggerType; + topSrc: SrcKeys; + bottomSrc: SrcKeys; + topColour: string; + bottomColour: string; + topBg: string; + bottomBg: string; + topSize: string; + bottomSize: string; + transition: number; + delay: number; + key: string; + lineColour: string; + lineHeight: string; +}; + +interface LowerProps { + eventNow: OntimeEvent | null; + viewSettings: ViewSettings; + lower: Message; +} + +const defaultOptions: Readonly = { + width: 45, + trigger: TriggerType.Event, + topSrc: SrcKeys.Title, + bottomSrc: SrcKeys.Subtitle, + topColour: '000000ff', + bottomColour: '000000ff', + topBg: '00000000', + bottomBg: '00000000', + topSize: '65px', + bottomSize: '40px', + transition: 3, + delay: 3, + key: 'ffffffff', + lineColour: 'ff0000ff', + lineHeight: '0.4em', +}; + +export default function LowerThird(props: LowerProps) { + const { eventNow, lower, viewSettings } = props; + const [searchParams] = useSearchParams(); + + const options = useMemo(() => { + const newOptions = { ...defaultOptions }; + + const width = searchParams.get('width'); + if (width !== null) { + newOptions.width = Number(width); + } + + const trigger = Object.values(TriggerType).find((s) => s === searchParams.get('trigger')); + if (trigger) { + newOptions.trigger = trigger; + } + + const topSrc = Object.values(SrcKeys).find((s) => s === searchParams.get('top-src')); + if (topSrc) { + newOptions.topSrc = topSrc; + } + + const bottomSrc = Object.values(SrcKeys).find((s) => s === searchParams.get('bottom-src')); + if (bottomSrc) { + newOptions.bottomSrc = bottomSrc; + } + + const topColour = searchParams.get('top-colour'); + if (topColour !== null) { + newOptions.topColour = topColour; + } + + const bottomColour = searchParams.get('bottom-colour'); + if (bottomColour !== null) { + newOptions.bottomColour = bottomColour; + } + + const topBg = searchParams.get('top-bg'); + if (topBg !== null) { + newOptions.topBg = topBg; + } + + const bottomBg = searchParams.get('bottom-bg'); + if (bottomBg !== null) { + newOptions.bottomBg = bottomBg; + } + + const topSize = searchParams.get('top-size'); + if (topSize !== null) { + newOptions.topSize = topSize; + } + + const bottomSize = searchParams.get('bottom-size'); + if (bottomSize && bottomSize != newOptions.bottomSize) { + newOptions.bottomSize = bottomSize; + } + + const transition = searchParams.get('transition'); + if (transition !== null) { + newOptions.transition = Number(transition); + } + + const delay = searchParams.get('delay'); + if (delay !== null) { + newOptions.delay = Number(delay); + } + + const key = searchParams.get('key'); + if (key !== null) { + newOptions.key = key; + } + + const lineColour = searchParams.get('line-colour'); + if (lineColour !== null) { + newOptions.lineColour = lineColour; + } + + return newOptions; + }, [searchParams]); + + useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); + + const [playState, setPlayState] = useState<'pre' | 'in' | 'out'>('pre'); + + useEffect(() => { + document.title = 'ontime - Lower Third'; + }, []); + + const topText = useMemo(() => { + if (options.topSrc === SrcKeys.LowerMsg) { + return lower.text; + } else if (eventNow) { + return eventNow[options.topSrc]; + } + return ''; + }, [eventNow, lower.text, options]); + + const bottomText = useMemo(() => { + if (options.bottomSrc === SrcKeys.LowerMsg) { + return lower.text; + } else if (eventNow) { + return eventNow[options.bottomSrc]; + } + return ''; + }, [eventNow, lower.text, options]); + + const transition = `${options.transition}s`; + + const trigger = useMemo(() => { + if (options.trigger === TriggerType.Event) { + return eventNow?.id; + } else if (options.trigger === TriggerType.Manual) { + return lower.visible; + } + return false; + }, [eventNow?.id, lower.visible, options.trigger]); + + useEffect(() => { + if (options.trigger === TriggerType.Event && trigger) { + setPlayState('in'); + const animateOutInMs = options.delay * 1000 + options.transition * 1000; + const timeout = setTimeout(() => { + setPlayState('out'); + }, animateOutInMs); + return () => clearTimeout(timeout); + } else if (options.trigger === TriggerType.Manual) { + setPlayState(trigger ? 'in' : 'out'); + } else { + setPlayState('pre'); + } + return () => null; + }, [options.delay, options.transition, options.trigger, trigger]); + + return ( +
+ + +
+
+
+ {topText} +
+
+
+
+
+ {bottomText} +
+
+
+
+ ); +} diff --git a/apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx b/apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx deleted file mode 100644 index 1540e5bb2..000000000 --- a/apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { memo, useEffect, useState } from 'react'; -import isEqual from 'react-fast-compare'; -import { useSearchParams } from 'react-router-dom'; -import { Message, OntimeEvent, ViewSettings } from 'ontime-types'; - -import { overrideStylesURL } from '../../../common/api/apiConstants'; -import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; - -import LowerLines from './LowerLines'; - -export type LowerOptions = { - size: number; - transitionIn: number; - textColour: string; - bgColour: string; - keyColour?: string; - fadeOut: number; -}; - -interface LowerProps { - eventNow: OntimeEvent | null; - lower: Message; - viewSettings: ViewSettings; -} - -// prevent triggering animation without a content change -const areEqual = (prevProps: LowerProps, nextProps: LowerProps) => { - return isEqual(prevProps.eventNow?.title, nextProps.eventNow?.title) && isEqual(prevProps.lower, nextProps.lower); -}; - -const Lower = (props: LowerProps) => { - const { eventNow, lower, viewSettings } = props; - const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); - const [searchParams] = useSearchParams(); - - const [heading, setHeading] = useState(''); - const [subheading, setSubheading] = useState(''); - const [showLower, setShowLower] = useState(false); - - // Set window title - useEffect(() => { - document.title = 'ontime - Lower Thirds'; - }, []); - - // reload if data changes - useEffect(() => { - // clear titles if necessary - // will trigger an animation out in the component - let timeout: NodeJS.Timeout; - - const haveTitlesChanged = eventNow?.title !== heading || eventNow?.presenter !== subheading; - const areTitlesEmpty = !eventNow?.title && !eventNow?.presenter; - - // we have new titles - if (haveTitlesChanged && !areTitlesEmpty) { - // show lower - setHeading(eventNow?.title ?? ''); - setSubheading(eventNow?.presenter ?? ''); - setShowLower(true); - - // schedule transition out - const transitionTime = 5000; - timeout = setTimeout(() => { - setShowLower(false); - }, transitionTime); - } - - return () => { - if (timeout) { - clearTimeout(timeout); - } - }; - // eslint-disable-next-line -- we do this to keep animations - }, [eventNow?.title, eventNow?.presenter]); - - // defer rendering until we load stylesheets - if (!shouldRender) { - return null; - } - - const options: LowerOptions = { - size: 1, - transitionIn: 3, - textColour: '#fffffa', - bgColour: '#00000033', - fadeOut: 3, - }; - - // size: multiplier - // Should be a number 0.0-n - const _size = searchParams.get('size'); - if (_size) { - const parsedValue = Number(_size); - if (!Number.isNaN(parsedValue)) { - options.size = parsedValue; - } - } - - // transitionIn: seconds - // Should be a number 0-n - const _transitionIn = searchParams.get('transition'); - if (_transitionIn) { - const parsedValue = Number(_transitionIn); - if (!Number.isNaN(parsedValue)) { - options.transitionIn = parsedValue; - } - } - - // textColour: string - // Should be a hex string '#ffffff' - const _textColour = searchParams.get('text'); - if (_textColour) options.textColour = `#${_textColour}`; - - // bgColour: string - // Should be a hex string '#ffffff' - const _bgColour = searchParams.get('bg'); - if (_bgColour) options.bgColour = `#${_bgColour}`; - - // key: string - // Should be a hex string '#00FF00' with key colour - const _key = searchParams.get('key'); - if (_key) options.keyColour = `#${_key}`; - - // fadeOut: seconds - // Should be a number 0-n - const _fadeOut = searchParams.get('fadeout'); - if (_fadeOut) { - const parsedValue = Number(_transitionIn); - if (!Number.isNaN(parsedValue)) { - options.fadeOut = parsedValue; - } - } - - return ; -}; - -export default memo(Lower, areEqual); diff --git a/apps/server/src/external/styles/override.css b/apps/server/src/external/styles/override.css index 61b45e6b5..054394010 100644 --- a/apps/server/src/external/styles/override.css +++ b/apps/server/src/external/styles/override.css @@ -24,6 +24,14 @@ --studio-active-label: #101010; --studio-idle-label: #595959; --studio-overtime: #101010; + + --lowerThird-font-family-override: 'Times New Roman'; + --lowerThird-top-font-weight-override: bold; + --lowerThird-bottom-font-weight-override: bold; + --lowerThird-top-font-style-override: normal; + --lowerThird-bottom-font-style-override: italic; + --lowerThird-line-height-override: 1vh; + --lowerThird-text-align-override: center; } .timer { diff --git a/e2e/tests/features/201-message-control.spec.ts b/e2e/tests/features/201-message-control.spec.ts index 59734f6d1..b8e22885d 100644 --- a/e2e/tests/features/201-message-control.spec.ts +++ b/e2e/tests/features/201-message-control.spec.ts @@ -20,7 +20,7 @@ test('message control sends messages to screens', async ({ context }) => { await editorPage.getByPlaceholder('Shown in lower third').fill('testing lower'); await editorPage.getByRole('button', { name: /toggle lower third message/i }).click(); - await featurePage.goto('http://localhost:4001/lower'); + await featurePage.goto('http://localhost:4001/lower?trigger=manual&bottom-src=lowerMsg'); await featurePage.waitForLoadState('load', { timeout: 5000 }); await featurePage.getByText('testing lower').click({ timeout: 5000 });