Skip to content

Commit 7ba4b71

Browse files
committed
Add links to fullscreen versions of screens from screens llist view
1 parent c7a2548 commit 7ba4b71

5 files changed

Lines changed: 85 additions & 19 deletions

File tree

packages/webui/src/client/ui/ClockView/CameraConfigForm.tsx

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,18 @@ import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
88
import { useSubscription, useTracker } from '../../lib/ReactMeteorData/ReactMeteorData.js'
99
import { ShowStyleBases } from '../../collections/index.js'
1010
import { applyAndValidateOverrides } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
11+
import { FullscreenLink } from './FullscreenLink.js'
1112

1213
import './PrompterConfigForm.scss'
1314

1415
interface CameraConfigState {
1516
selectedSourceLayerIds: Set<string>
1617
studioLabels: string
17-
fullscreen: boolean
1818
}
1919

2020
const initialState: CameraConfigState = {
2121
selectedSourceLayerIds: new Set(),
2222
studioLabels: '',
23-
fullscreen: false,
2423
}
2524

2625
/** Source layer types that are relevant for the camera screen */
@@ -36,9 +35,6 @@ function generateCameraUrl(studioId: StudioId, config: CameraConfigState): strin
3635
if (config.studioLabels.trim()) {
3736
params.set('studioLabels', config.studioLabels.trim())
3837
}
39-
if (config.fullscreen) {
40-
params.set('fullscreen', '1')
41-
}
4238

4339
const queryString = params.toString()
4440
return `/countdowns/${studioId}/camera${queryString ? '?' + queryString : ''}`
@@ -157,17 +153,6 @@ export function CameraConfigForm({ studioId }: Readonly<{ studioId: StudioId }>)
157153
{t('Comma-separated list of studio labels to filter by. Leave empty for all.')}
158154
</Form.Text>
159155
</Form.Group>
160-
161-
<Form.Group className="mb-2">
162-
<Form.Check
163-
type="checkbox"
164-
id="camera-fullscreen"
165-
label={t('Fullscreen mode')}
166-
checked={config.fullscreen}
167-
onChange={(e) => updateConfig('fullscreen', e.target.checked)}
168-
/>
169-
<Form.Text className="text-muted">{t('Click anywhere on the screen to enter fullscreen.')}</Form.Text>
170-
</Form.Group>
171156
</div>
172157

173158
{/* Generated URL and Open Button */}
@@ -178,9 +163,12 @@ export function CameraConfigForm({ studioId }: Readonly<{ studioId: StudioId }>)
178163
</Form.Label>
179164
<Form.Control type="text" size="sm" readOnly value={generatedUrl} onClick={(e) => e.currentTarget.select()} />
180165
</Form.Group>
181-
<Link to={generatedUrl} className="btn btn-primary">
166+
<Link to={generatedUrl} className="btn btn-primary me-2">
182167
{t('Open Camera Screen')}
183168
</Link>
169+
<FullscreenLink to={generatedUrl} className="btn btn-secondary">
170+
{t('Open Fullscreen')}
171+
</FullscreenLink>
184172
</div>
185173
</div>
186174
)

packages/webui/src/client/ui/ClockView/ClockViewIndex.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Accordion from 'react-bootstrap/esm/Accordion'
77
import { PresenterConfigForm } from './PresenterConfigForm'
88
import { CameraConfigForm } from './CameraConfigForm'
99
import { PrompterConfigForm } from './PrompterConfigForm'
10+
import { FullscreenLink } from './FullscreenLink'
1011

1112
type AccordionKey = 'presenter' | 'camera' | 'prompter'
1213

@@ -38,12 +39,21 @@ export function ClockViewIndex({ studioId }: Readonly<{ studioId: StudioId }>):
3839
<ul>
3940
<li>
4041
<Link to={`/countdowns/${studioId}/director`}>{t('Director Screen')}</Link>
42+
{' ('}
43+
<FullscreenLink to={`/countdowns/${studioId}/director`}>{t('fullscreen')}</FullscreenLink>
44+
{')'}
4145
</li>
4246
<li>
4347
<Link to={`/countdowns/${studioId}/overlay`}>{t('Overlay Screen')}</Link>
48+
{' ('}
49+
<FullscreenLink to={`/countdowns/${studioId}/overlay`}>{t('fullscreen')}</FullscreenLink>
50+
{')'}
4451
</li>
4552
<li>
4653
<Link to={`/countdowns/${studioId}/multiview`}>{t('All Screens in a MultiViewer')}</Link>
54+
{' ('}
55+
<FullscreenLink to={`/countdowns/${studioId}/multiview`}>{t('fullscreen')}</FullscreenLink>
56+
{')'}
4757
</li>
4858
<li>
4959
<Link to={`/activeRundown/${studioId}`}>{t('Active Rundown View')}</Link>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useCallback, MouseEvent } from 'react'
2+
import { Link, useHistory } from 'react-router-dom'
3+
import { catchError } from '../../lib/lib.js'
4+
5+
interface FullscreenLinkProps {
6+
to: string
7+
children: React.ReactNode
8+
className?: string
9+
}
10+
11+
/**
12+
* Appends fullscreen=1 to a URL path, handling existing query strings.
13+
*/
14+
function addFullscreenParam(url: string): string {
15+
const hasQuery = url.includes('?')
16+
return hasQuery ? `${url}&fullscreen=1` : `${url}?fullscreen=1`
17+
}
18+
19+
/**
20+
* A link that navigates to a destination and also triggers fullscreen mode.
21+
* Regular clicks will navigate AND trigger fullscreen.
22+
* Cmd-click, Ctrl-click, or middle-click will open in a new tab (normal link behavior).
23+
* The URL will include ?fullscreen=1 so the FullscreenOverlay can prompt for fullscreen if needed.
24+
*/
25+
export function FullscreenLink({ to, children, className }: Readonly<FullscreenLinkProps>): JSX.Element {
26+
const history = useHistory()
27+
const fullscreenUrl = addFullscreenParam(to)
28+
29+
const handleClick = useCallback(
30+
(e: MouseEvent<HTMLAnchorElement>) => {
31+
// Allow normal link behavior for modifier keys or non-left clicks
32+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) {
33+
return
34+
}
35+
36+
e.preventDefault()
37+
38+
// Request fullscreen first, then navigate
39+
document.documentElement
40+
.requestFullscreen({
41+
navigationUI: 'hide',
42+
})
43+
.then(() => {
44+
history.push(fullscreenUrl)
45+
})
46+
.catch((err) => {
47+
// If fullscreen fails (e.g., not allowed), still navigate
48+
catchError('FullscreenLink.requestFullscreen')(err)
49+
history.push(fullscreenUrl)
50+
})
51+
},
52+
[fullscreenUrl, history]
53+
)
54+
55+
return (
56+
<Link to={fullscreenUrl} onClick={handleClick} className={className}>
57+
{children}
58+
</Link>
59+
)
60+
}

packages/webui/src/client/ui/ClockView/PresenterConfigForm.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { MeteorPubSub } from '@sofie-automation/meteor-lib/dist/api/pubsub'
88
import { useSubscription, useTracker } from '../../lib/ReactMeteorData/ReactMeteorData.js'
99
import { RundownLayouts } from '../../collections/index.js'
1010
import { RundownLayoutsAPI } from '../../lib/rundownLayouts.js'
11+
import { FullscreenLink } from './FullscreenLink.js'
1112

1213
import './PrompterConfigForm.scss'
1314

@@ -86,9 +87,12 @@ export function PresenterConfigForm({ studioId }: Readonly<{ studioId: StudioId
8687
</Form.Label>
8788
<Form.Control type="text" size="sm" readOnly value={generatedUrl} onClick={(e) => e.currentTarget.select()} />
8889
</Form.Group>
89-
<Link to={generatedUrl} className="btn btn-primary">
90+
<Link to={generatedUrl} className="btn btn-primary me-2">
9091
{t('Open Presenter Screen')}
9192
</Link>
93+
<FullscreenLink to={generatedUrl} className="btn btn-secondary">
94+
{t('Open Fullscreen')}
95+
</FullscreenLink>
9296
</div>
9397
</div>
9498
)

packages/webui/src/client/ui/ClockView/PrompterConfigForm.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
44
import { StudioId } from '@sofie-automation/corelib/dist/dataModel/Ids'
55
import Form from 'react-bootstrap/esm/Form'
66
import Collapse from 'react-bootstrap/esm/Collapse'
7+
import { FullscreenLink } from './FullscreenLink.js'
78

89
import './PrompterConfigForm.scss'
910

@@ -668,9 +669,12 @@ export function PrompterConfigForm({ studioId }: Readonly<{ studioId: StudioId }
668669
</Form.Label>
669670
<Form.Control type="text" size="sm" readOnly value={generatedUrl} onClick={(e) => e.currentTarget.select()} />
670671
</Form.Group>
671-
<Link to={generatedUrl} className="btn btn-primary">
672+
<Link to={generatedUrl} className="btn btn-primary me-2">
672673
{t('Open Prompter')}
673674
</Link>
675+
<FullscreenLink to={generatedUrl} className="btn btn-secondary">
676+
{t('Open Fullscreen')}
677+
</FullscreenLink>
674678
</div>
675679
</div>
676680
)

0 commit comments

Comments
 (0)