Skip to content

Commit 82e2a5d

Browse files
authored
Merge pull request Sofie-Automation#1757 from bbc/feat/unattended-active-rundown-view
feat: unattended active rundown view
2 parents c29c1a7 + 44b581f commit 82e2a5d

7 files changed

Lines changed: 59 additions & 9 deletions

File tree

packages/documentation/docs/for-developers/url-query-parameters.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ Appending query parameter(s) to the URL will allow you to modify the behaviour o
2323
| `vibrate=1` | Experimental feature that triggers the vibration API in the web browser 3 seconds before each planned _Take_. _Default value is `0`._ |
2424
| `zoom=100,...` | Sets the scaling of the entire GUI. _The unit is a percentage where `100` is the default scaling._ **Passing any `zoom` parameter will cause it to be stored in the browser's local storage as `uiZoomLevel` and will then be used for future sessions without notifying the user that they are using the Sofie GUI at a non-standard size!** |
2525
| `hideRundownHeader=1` | Hides header on [Rundown view](../user-guide/features/sofie-views-and-screens#rundown-view) and [Active Rundown screen](../user-guide/features/sofie-views-and-screens#active-rundown-screen). _Default value is `0`._ |
26+
| `lockView=1` | Locks the [Active Rundown screen](../user-guide/features/sofie-views-and-screens#active-rundown-screen) for unattended or kiosk use: hides exit controls (header close button and context menu “Close Rundown”), disables the in-app navigation confirmation when a rundown is active, and shows the [Screensaver](../user-guide/features/sofie-views-and-screens#screensaver) when no rundown is active instead of a message with a link back to the lobby. Only applies on `/activeRundown/:studioId` routes (ignored on `/rundown/:playlistId`). _Default value is `0`._ |

packages/documentation/docs/user-guide/features/sofie-views-and-screens.mdx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,20 @@ Example: [http://127.0.0.1/countdowns/studio0/camera?sourceLayerIds=camera0,dve0
347347

348348
A page which automatically displays the currently active rundown. Can be useful for the producer to have on a secondary screen.
349349

350+
When no rundown is active, a message is shown with a link back to the rundown list.
351+
352+
This screen can be configured using query parameters:
353+
354+
| Query parameter | Type | Description | Default |
355+
| :-------------- | :---- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ |
356+
| `lockView` | 0 / 1 | Locks the view for unattended or kiosk displays. Hides the header close button and the context menu “Close Rundown” action, and disables the in-app navigation confirmation when a rundown is active. When no rundown is active, shows the [Screensaver](#screensaver) instead of the default idle message. Does not block browser back, manual URL changes, or closing the tab. Only applies on Active Rundown routes (not on a specific `/rundown/:playlistId` URL). | `0` |
357+
358+
Example (locked Active Rundown for a secondary monitor):
359+
360+
`http://localhost:3000/activeRundown/studio0?lockView=1`
361+
362+
Other query parameters for the rundown view itself (such as `hideRundownHeader` and layout selection) can be combined with `lockView`. See [URL Query Parameters](../../for-developers/url-query-parameters.md).
363+
350364
### Active Rundown Shelf Screen
351365

352366
`/activeRundown/:studioId/shelf`
@@ -357,6 +371,10 @@ A screen which automatically displays the currently active rundown, and shows th
357371

358372
A shelf layout can be selected by modifying the query string, see [Shelf Layouts](#shelf-layouts).
359373

374+
The `lockView` parameter described above also applies to this route. Example:
375+
376+
`http://localhost:3000/activeRundown/studio0/shelf?lockView=1&layout=Stream`
377+
360378
### Specific Rundown Shelf Screen
361379

362380
`/rundown/:rundownId/shelf`
@@ -379,7 +397,7 @@ Each embedded screen shows a label to identify it. This screen is mostly intende
379397

380398
### Screensaver
381399

382-
When big screen displays \(like Prompter Screen and the Presenter Screen\) do not have any meaningful content to show, an animated screensaver showing the current time and the next planned show will be displayed. If no Rundown is upcoming, the Studio name will be displayed.
400+
When big screen displays \(like Prompter Screen, the Presenter Screen, and the Active Rundown Screen with `lockView=1`\) do not have any meaningful content to show, an animated screensaver showing the current time and the next planned show will be displayed. If no Rundown is upcoming, the Studio name will be displayed.
383401

384402
![A screensaver showing the next scheduled show](/img/docs/main/features/next-scheduled-show-example.png)
385403

packages/webui/src/client/ui/ActiveRundownView.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { NavLink, Route, Switch, useRouteMatch } from 'react-router-dom'
1+
import { NavLink, Route, Switch, useLocation, useRouteMatch } from 'react-router-dom'
2+
import { parse as queryStringParse } from 'query-string'
23
import { useSubscription, useTracker } from '../lib/ReactMeteorData/ReactMeteorData.js'
34

45
import { Spinner } from '../lib/Spinner.js'
56
import { RundownView } from './RundownView.js'
7+
import { StudioScreenSaver } from './StudioScreenSaver/StudioScreenSaver.js'
68
import { MeteorPubSub } from '@sofie-automation/meteor-lib/dist/api/pubsub'
79
import { UIStudios } from './Collections.js'
810
import type { StudioId } from '@sofie-automation/corelib/dist/dataModel/Ids'
@@ -14,6 +16,8 @@ export function ActiveRundownView({ studioId }: Readonly<{ studioId: StudioId }>
1416
const { t } = useTranslation()
1517

1618
const { path } = useRouteMatch()
19+
const { search } = useLocation()
20+
const lockView = queryStringParse(search)['lockView'] === '1'
1721

1822
const studioReady = useSubscription(MeteorPubSub.uiStudio, studioId)
1923
const playlistReady = useSubscription(MeteorPubSub.rundownPlaylistForStudio, studioId, true)
@@ -48,6 +52,9 @@ export function ActiveRundownView({ studioId }: Readonly<{ studioId: StudioId }>
4852
</Switch>
4953
)
5054
} else if (studio) {
55+
if (lockView) {
56+
return <StudioScreenSaver studioId={studioId} ownBackground={true} screenName={t('Rundown View')} />
57+
}
5158
return <NotFoundMessage message={t('There is no rundown active in this studio.')} />
5259
} else if (studioId) {
5360
return <NotFoundMessage message={t("This studio doesn't exist.")} />

packages/webui/src/client/ui/RundownView.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ export function RundownView(props: Readonly<IProps>): JSX.Element {
288288
)
289289

290290
const hideRundownHeader = params['hideRundownHeader'] === '1'
291+
const lockView = props.inActiveRundownView && params['lockView'] === '1'
291292

292293
return (
293294
<div
@@ -314,6 +315,7 @@ export function RundownView(props: Readonly<IProps>): JSX.Element {
314315
uiSegmentMap={miniShelfData.uiSegmentMap}
315316
miniShelfFilter={miniShelfData.miniShelfFilter}
316317
hideRundownHeader={hideRundownHeader}
318+
lockView={lockView}
317319
/>
318320
</div>
319321
)
@@ -323,6 +325,7 @@ interface IPropsWithReady extends IProps {
323325
subsReady: boolean
324326
userPermissions: Readonly<UserPermissions>
325327
hideRundownHeader?: boolean
328+
lockView?: boolean
326329
}
327330

328331
interface IRundownViewContentSnapshot {
@@ -1410,6 +1413,7 @@ const RundownViewContent = translateWithTracker<IPropsWithReady & ITrackedProps,
14101413
firstRundown={this.props.rundowns[0]}
14111414
currentRundown={currentRundown}
14121415
rundownCount={this.props.rundowns.length}
1416+
lockView={this.props.lockView}
14131417
/>
14141418
</ErrorBoundary>
14151419
)}
@@ -1516,7 +1520,7 @@ const RundownViewContent = translateWithTracker<IPropsWithReady & ITrackedProps,
15161520
<ErrorBoundary>
15171521
{this.props.userPermissions.studio && (
15181522
<Prompt
1519-
when={!!playlist.activationId}
1523+
when={!!playlist.activationId && !this.props.lockView}
15201524
message={t('This rundown is now active. Are you sure you want to exit this screen?')}
15211525
/>
15221526
)}

packages/webui/src/client/ui/RundownView/RundownHeader/RundownContextMenu.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface RundownContextMenuProps {
2626
playlist: DBRundownPlaylist
2727
studio: UIStudio
2828
firstRundown: Rundown | undefined
29+
lockView?: boolean
2930
onShow?: () => void
3031
onHide?: () => void
3132
}
@@ -39,6 +40,7 @@ export function RundownContextMenu({
3940
playlist,
4041
studio,
4142
firstRundown,
43+
lockView,
4244
onShow,
4345
onHide,
4446
}: Readonly<RundownContextMenuProps>): JSX.Element {
@@ -133,8 +135,12 @@ export function RundownContextMenu({
133135
})}
134136
</MenuItem>
135137
<MenuItem onClick={operations.takeRundownSnapshot}>{t('Store Snapshot')}</MenuItem>
136-
<MenuItem divider />
137-
<MenuItem onClick={() => history.push('/')}>{t('Close Rundown')}</MenuItem>
138+
{!lockView && (
139+
<>
140+
<MenuItem divider />
141+
<MenuItem onClick={() => history.push('/')}>{t('Close Rundown')}</MenuItem>
142+
</>
143+
)}
138144
</React.Fragment>
139145
) : (
140146
<React.Fragment>

packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,11 @@
536536
flex-shrink: 0;
537537
transition: opacity 0.2s;
538538

539+
&--placeholder {
540+
visibility: hidden;
541+
pointer-events: none;
542+
}
543+
539544
svg,
540545
i {
541546
filter: drop-shadow(0 0 0 rgba(255, 255, 255, 0));
@@ -578,7 +583,7 @@
578583
color: #fff;
579584
}
580585

581-
.rundown-header__close-btn {
586+
.rundown-header__close-btn:not(.rundown-header__close-btn--placeholder) {
582587
opacity: 1;
583588
}
584589

packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface IRundownHeaderProps {
2727
studio: UIStudio
2828
firstRundown: Rundown | undefined
2929
rundownCount: number
30+
lockView?: boolean
3031
}
3132

3233
export function RundownHeader({
@@ -35,6 +36,7 @@ export function RundownHeader({
3536
firstRundown,
3637
currentRundown,
3738
rundownCount,
39+
lockView,
3840
}: IRundownHeaderProps): JSX.Element {
3941
const { t } = useTranslation()
4042
const timingDurations = useTiming()
@@ -80,6 +82,7 @@ export function RundownHeader({
8082
playlist={playlist}
8183
studio={studio}
8284
firstRundown={firstRundown}
85+
lockView={lockView}
8386
onShow={() => setIsContextMenuOpen(true)}
8487
onHide={() => {
8588
setIsMenuOpen(false)
@@ -157,9 +160,15 @@ export function RundownHeader({
157160
<RundownHeaderDurations playlist={playlist} simplified={simplified} />
158161
<RundownHeaderExpectedEnd playlist={playlist} simplified={simplified} />
159162
</button>
160-
<NavLink to="/" title={t('Exit')} aria-label={t('Exit')} className="rundown-header__close-btn">
161-
<FontAwesomeIcon icon="close" size="xl" />
162-
</NavLink>
163+
{lockView ? (
164+
<span className="rundown-header__close-btn rundown-header__close-btn--placeholder" aria-hidden="true">
165+
<FontAwesomeIcon icon="close" size="xl" />
166+
</span>
167+
) : (
168+
<NavLink to="/" title={t('Exit')} aria-label={t('Exit')} className="rundown-header__close-btn">
169+
<FontAwesomeIcon icon="close" size="xl" />
170+
</NavLink>
171+
)}
163172
</div>
164173
</div>
165174
</RundownHeaderContextMenuTrigger>

0 commit comments

Comments
 (0)