From 766ff40baad65ae9e40b702c2f35723c278f2bf1 Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Mon, 17 Oct 2022 11:34:18 +0200 Subject: [PATCH] feat: allow rotating viewer (#220) * feat: allow viewer mirroring --- client/package.json | 2 +- client/src/common/atoms/ViewerSettings.ts | 3 +++ client/src/common/components/nav/NavLogo.jsx | 12 ++++++++++++ client/src/features/viewers/backstage/Backstage.jsx | 5 ++++- client/src/features/viewers/clock/Clock.tsx | 10 +++++----- client/src/features/viewers/countdown/Countdown.jsx | 7 +++++-- .../features/viewers/minimal-timer/MinimalTimer.tsx | 7 ++++++- .../src/features/viewers/picture-in-picture/Pip.jsx | 5 ++++- client/src/features/viewers/public/Public.jsx | 5 ++++- client/src/features/viewers/studio/StudioClock.jsx | 10 +++++++--- client/src/features/viewers/timer/Timer.jsx | 6 +++++- client/src/theme/_viewerCommon.scss | 3 +++ client/src/theme/_viewerDefs.scss | 1 + server/package.json | 2 +- 14 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 client/src/common/atoms/ViewerSettings.ts create mode 100644 client/src/theme/_viewerCommon.scss diff --git a/client/package.json b/client/package.json index ecde8d3ae..b13f7f572 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "ontime-ui", - "version": "1.8.5", + "version": "1.9.0", "private": true, "dependencies": { "@chakra-ui/react": "^2.3.2", diff --git a/client/src/common/atoms/ViewerSettings.ts b/client/src/common/atoms/ViewerSettings.ts new file mode 100644 index 000000000..deeb3fb22 --- /dev/null +++ b/client/src/common/atoms/ViewerSettings.ts @@ -0,0 +1,3 @@ +import { atomWithStorage } from 'jotai/utils'; + +export const mirrorViewersAtom = atomWithStorage('ontime-viewers-mirrorViewers', false); diff --git a/client/src/common/components/nav/NavLogo.jsx b/client/src/common/components/nav/NavLogo.jsx index 110d6b62f..1fb8899bb 100644 --- a/client/src/common/components/nav/NavLogo.jsx +++ b/client/src/common/components/nav/NavLogo.jsx @@ -4,10 +4,13 @@ import { IconButton } from '@chakra-ui/button'; import { Image } from '@chakra-ui/react'; import { IoContract } from '@react-icons/all-files/io5/IoContract'; import { IoExpand } from '@react-icons/all-files/io5/IoExpand'; +import { IoSync } from '@react-icons/all-files/io5/IoSync'; import navlogo from 'assets/images/logos/LOGO-72.png'; import { AnimatePresence, motion } from 'framer-motion'; +import { useAtom } from 'jotai'; import PropTypes from 'prop-types'; +import { mirrorViewersAtom } from '../../atoms/ViewerSettings'; import useFullscreen from '../../hooks/useFullscreen'; import navigatorConstants from './navigatorConstants'; @@ -26,6 +29,7 @@ export default function NavLogo(props) { const { isHidden } = props; const [showNav, setShowNav] = useState(false); const { isFullScreen, toggleFullScreen } = useFullscreen(); + const [isMirrored, setMirrored] = useAtom(mirrorViewersAtom); const handleClick = useCallback(() => { setShowNav((prev) => !prev); @@ -57,6 +61,8 @@ export default function NavLogo(props) { tabIndex: 0, }; + console.log('debug', isMirrored) + return ( + } + onClick={() => setMirrored((prev) => !prev)} + {...navButtonStyle} + /> : } diff --git a/client/src/features/viewers/backstage/Backstage.jsx b/client/src/features/viewers/backstage/Backstage.jsx index 915bcede8..080da640a 100644 --- a/client/src/features/viewers/backstage/Backstage.jsx +++ b/client/src/features/viewers/backstage/Backstage.jsx @@ -5,9 +5,11 @@ import Paginator from 'common/components/paginator/Paginator'; import TitleSide from 'common/components/title-side/TitleSide'; import { formatDisplay } from 'common/utils/dateConfig'; import { AnimatePresence, motion } from 'framer-motion'; +import { useAtom } from 'jotai'; import PropTypes from 'prop-types'; import { overrideStylesURL } from '../../../common/api/apiConstants'; +import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { getEventsWithDelay } from '../../../common/utils/eventsManager'; import { formatTime } from '../../../common/utils/time'; @@ -26,6 +28,7 @@ export default function Backstage(props) { const [filteredEvents, setFilteredEvents] = useState(null); const [pageNumber, setPageNumber] = useState(0); const [currentPage, setCurrentPage] = useState(0); + const [isMirrored] = useAtom(mirrorViewersAtom); // Set window title useEffect(() => { @@ -58,7 +61,7 @@ export default function Backstage(props) { const clock = formatTime(time.clock, formatOptions); return ( -
+
{general.title}
diff --git a/client/src/features/viewers/clock/Clock.tsx b/client/src/features/viewers/clock/Clock.tsx index 17d6b39c3..3d9be7a5e 100644 --- a/client/src/features/viewers/clock/Clock.tsx +++ b/client/src/features/viewers/clock/Clock.tsx @@ -1,13 +1,12 @@ import { useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; +import { useAtom } from 'jotai'; import { overrideStylesURL } from '../../../common/api/apiConstants'; +import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import NavLogo from '../../../common/components/nav/NavLogo'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { - TimeManager, - ViewSettings, -} from '../../../common/models/OntimeTypes'; +import { TimeManager, ViewSettings } from '../../../common/models/OntimeTypes'; import { OverridableOptions } from '../../../common/models/ViewTypes'; import { formatTime } from '../../../common/utils/time'; @@ -27,6 +26,7 @@ export default function Clock(props: ClockProps) { const { time, viewSettings } = props; const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const [searchParams] = useSearchParams(); + const [isMirrored] = useAtom(mirrorViewersAtom); useEffect(() => { document.title = 'ontime - Clock'; @@ -127,7 +127,7 @@ export default function Clock(props: ClockProps) { return (
+
{follow === null ? (
@@ -139,7 +142,7 @@ export default function Countdown(props) { > {formatDisplay( isSelected ? runningTimer : runningTimer + millisToSeconds(delay), - isSelected || time.waiting + isSelected || time.waiting, )}
{follow?.title || 'Untitled Event'}
diff --git a/client/src/features/viewers/minimal-timer/MinimalTimer.tsx b/client/src/features/viewers/minimal-timer/MinimalTimer.tsx index ea723c158..10c29f223 100644 --- a/client/src/features/viewers/minimal-timer/MinimalTimer.tsx +++ b/client/src/features/viewers/minimal-timer/MinimalTimer.tsx @@ -1,7 +1,9 @@ import { useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; +import { useAtom } from 'jotai'; import { overrideStylesURL } from '../../../common/api/apiConstants'; +import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import NavLogo from '../../../common/components/nav/NavLogo'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { @@ -24,6 +26,7 @@ export default function MinimalTimer(props: MinimalTimerProps) { const { pres, time, viewSettings } = props; const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const [searchParams] = useSearchParams(); + const [isMirrored] = useAtom(mirrorViewersAtom); useEffect(() => { document.title = 'ontime - Minimal Timer'; @@ -131,9 +134,11 @@ export default function MinimalTimer(props: MinimalTimerProps) { const clean = timer.replace('/:/g', ''); const showFinished = time.isNegative && !userOptions?.hideOvertime; + const baseClasses = `minimal-timer ${isMirrored ? 'mirror' : ''}`; + return (
{ @@ -65,7 +68,7 @@ export default function Pip(props) { const clock = formatTime(time.clock, formatOptions); return ( -
+
{general.title}
diff --git a/client/src/features/viewers/public/Public.jsx b/client/src/features/viewers/public/Public.jsx index 07f410da1..3a871e3f1 100644 --- a/client/src/features/viewers/public/Public.jsx +++ b/client/src/features/viewers/public/Public.jsx @@ -4,9 +4,11 @@ import NavLogo from 'common/components/nav/NavLogo'; import Paginator from 'common/components/paginator/Paginator'; import TitleSide from 'common/components/title-side/TitleSide'; import { AnimatePresence, motion } from 'framer-motion'; +import { useAtom } from 'jotai'; import PropTypes from 'prop-types'; import { overrideStylesURL } from '../../../common/api/apiConstants'; +import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { formatTime } from '../../../common/utils/time'; import { titleVariants } from '../common/animation'; @@ -23,6 +25,7 @@ export default function Public(props) { const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const [pageNumber, setPageNumber] = useState(0); const [currentPage, setCurrentPage] = useState(0); + const [isMirrored] = useAtom(mirrorViewersAtom); // Set window title useEffect(() => { @@ -40,7 +43,7 @@ export default function Public(props) { const clock = formatTime(time.clock, formatOptions); return ( -
+
{general.title}
diff --git a/client/src/features/viewers/studio/StudioClock.jsx b/client/src/features/viewers/studio/StudioClock.jsx index 74688657e..b5da9b5e7 100644 --- a/client/src/features/viewers/studio/StudioClock.jsx +++ b/client/src/features/viewers/studio/StudioClock.jsx @@ -1,7 +1,9 @@ import { useEffect, useState } from 'react'; +import { useAtom } from 'jotai'; import PropTypes from 'prop-types'; import { overrideStylesURL } from '../../../common/api/apiConstants'; +import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import NavLogo from '../../../common/components/nav/NavLogo'; import useFitText from '../../../common/hooks/useFitText'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; @@ -28,6 +30,7 @@ export default function StudioClock(props) { const { fontSize: titleFontSize, ref: titleRef } = useFitText({ maxFontSize: 500 }); const [schedule, setSchedule] = useState([]); + const [isMirrored] = useAtom(mirrorViewersAtom); const activeIndicators = [...Array(12).keys()]; const secondsIndicators = [...Array(60).keys()]; @@ -48,13 +51,13 @@ export default function StudioClock(props) { showEnd: false, }); setSchedule(formatted); - }, [backstageEvents, nextId, selectedId] ); + }, [backstageEvents, nextId, selectedId]); const clock = formatTime(time.clock, formatOptions); const [, , secondsNow] = stringFromMillis(time.clock).split(':'); return ( -
+
{clock}
@@ -65,7 +68,8 @@ export default function StudioClock(props) { > {title.titleNext}
-
+
{selectedId != null && formatDisplay(time.running)}
diff --git a/client/src/features/viewers/timer/Timer.jsx b/client/src/features/viewers/timer/Timer.jsx index 2e5e8f1e2..b639a5bb2 100644 --- a/client/src/features/viewers/timer/Timer.jsx +++ b/client/src/features/viewers/timer/Timer.jsx @@ -5,9 +5,11 @@ import MyProgressBar from 'common/components/myProgressBar/MyProgressBar'; import NavLogo from 'common/components/nav/NavLogo'; import TitleCard from 'common/components/title-card/TitleCard'; import { AnimatePresence, motion } from 'framer-motion'; +import { useAtom } from 'jotai'; import PropTypes from 'prop-types'; import { overrideStylesURL } from '../../../common/api/apiConstants'; +import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { formatTime } from '../../../common/utils/time'; @@ -23,6 +25,7 @@ export default function Timer(props) { const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const [elapsed, setElapsed] = useState(true); const [searchParams] = useSearchParams(); + const [isMirrored] = useAtom(mirrorViewersAtom); useEffect(() => { document.title = 'ontime - Timer'; @@ -64,9 +67,10 @@ export default function Timer(props) { y: 500, }, }; + const baseClasses = `stage-timer ${isMirrored ? 'mirror' : ''}`; return ( -
+
{pres.text}
diff --git a/client/src/theme/_viewerCommon.scss b/client/src/theme/_viewerCommon.scss new file mode 100644 index 000000000..d14ad5915 --- /dev/null +++ b/client/src/theme/_viewerCommon.scss @@ -0,0 +1,3 @@ +.mirror { + transform: rotate(180deg); +} diff --git a/client/src/theme/_viewerDefs.scss b/client/src/theme/_viewerDefs.scss index 067dbab38..5b91b2085 100644 --- a/client/src/theme/_viewerDefs.scss +++ b/client/src/theme/_viewerDefs.scss @@ -1,4 +1,5 @@ @use 'main' as *; +@use 'viewerCommon' as *; // General styling $accent-color: $ontime-pink; // --accent-color-override diff --git a/server/package.json b/server/package.json index 6755a72d6..28c042d9e 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "ontime", - "version": "1.8.5", + "version": "1.9.0", "author": "Carlos Valente", "description": "Time keeping for live events", "repository": "https://github.com/cpvalente/ontime",