Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

Commit 1d563f4

Browse files
committed
feat(Output): create a hook for handling scrolling
1 parent 17b0fdb commit 1d563f4

File tree

3 files changed

+64
-27
lines changed

3 files changed

+64
-27
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { useEffect, useRef } from 'react'
2+
import { RootAppStore } from 'src/stores/RootAppStore'
3+
4+
export function useControllerMessages(ref: React.RefObject<HTMLElement>, heightPx: number, fontSizePx: number) {
5+
const speed = useRef(0)
6+
const position = useRef(0)
7+
const lastRequest = useRef<number | null>(null)
8+
const lastFrameTime = useRef<number | null>(null)
9+
10+
useEffect(() => {
11+
const onMessage = (message: { speed: number }) => {
12+
console.log('received message', message)
13+
14+
speed.current = message.speed
15+
}
16+
17+
RootAppStore.connection.controller.on('message', onMessage)
18+
RootAppStore.connection.controller.subscribeToMessages().catch(console.error)
19+
20+
const onFrame = (now: number) => {
21+
const frameTime = lastFrameTime.current === null ? 16 : now - lastFrameTime.current
22+
const scrollBy = ((speed.current * fontSizePx) / 300) * frameTime
23+
position.current = Math.max(0, position.current + scrollBy)
24+
console.log(position.current)
25+
26+
ref.current?.scrollTo(0, position.current)
27+
28+
lastFrameTime.current = now
29+
lastRequest.current = window.requestAnimationFrame(onFrame)
30+
}
31+
32+
lastRequest.current = window.requestAnimationFrame(onFrame)
33+
34+
return () => {
35+
RootAppStore.connection.controller.off('message', onMessage)
36+
37+
if (lastRequest.current !== null) window.cancelAnimationFrame(lastRequest.current)
38+
}
39+
}, [ref, heightPx, fontSizePx])
40+
}

packages/apps/client/src/views/Output/Output.tsx

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
1+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
22
import { observer } from 'mobx-react-lite'
33
import { RootAppStore } from 'src/stores/RootAppStore.ts'
44

@@ -9,41 +9,38 @@ import { getCurrentTime } from 'src/lib/getCurrentTime'
99
import { useQueryParam } from 'src/lib/useQueryParam'
1010

1111
import classes from './Output.module.scss'
12+
import { useControllerMessages } from 'src/hooks/useControllerMessages'
1213

1314
const Output = observer(function Output(): React.ReactElement {
1415
const rootEl = useRef<HTMLDivElement>(null)
15-
const speed = useRef(0)
16+
const [size, setSize] = useState({ height: window.innerHeight, width: window.innerWidth })
1617

1718
const isPrimary = useQueryParam('primary') !== null
1819

1920
// On startup
2021
useEffect(() => {
2122
RootAppStore.outputSettingsStore.initialize() // load and subscribe
23+
})
2224

23-
RootAppStore.connection.controller.on('message', (message) => {
24-
console.log('received message', message)
25-
26-
speed.current = message.speed
27-
})
28-
RootAppStore.connection.controller.subscribeToMessages().catch(console.error)
25+
const outputSettings = RootAppStore.outputSettingsStore.outputSettings
2926

30-
// don't do this, it's just for testing:
31-
const interval = setInterval(() => {
32-
rootEl.current?.scrollBy(0, speed.current)
33-
}, 1000 / 60)
27+
const fontSize = outputSettings.fontSize
28+
const scaleVertical = outputSettings.mirrorVertically ? '-1' : '1'
29+
const scaleHorizontal = outputSettings.mirrorHorizontally ? '-1' : '1'
3430

35-
return () => {
36-
RootAppStore.connection.controller.off('message')
37-
clearInterval(interval)
38-
}
39-
}, [])
31+
useControllerMessages(rootEl, size.height, (fontSize * size.width) / 100)
4032

4133
const onViewPortSizeChanged = useCallback(() => {
34+
const width = window.innerWidth
35+
const height = window.innerHeight
36+
const aspectRatio = width / height
37+
setSize({ width, height })
38+
4239
if (!isPrimary) return
4340

4441
RootAppStore.connection.viewPort.update(null, {
4542
_id: '',
46-
aspectRatio: window.innerWidth / window.innerHeight,
43+
aspectRatio,
4744
// TODO: This should return the actual lastKnownState
4845
lastKnownState: {
4946
timestamp: getCurrentTime(),
@@ -65,8 +62,6 @@ const Output = observer(function Output(): React.ReactElement {
6562
}
6663
}, [onViewPortSizeChanged])
6764

68-
const outputSettings = RootAppStore.outputSettingsStore.outputSettings
69-
7065
const activeRundownPlaylistId = outputSettings?.activeRundownPlaylistId
7166

7267
useEffect(() => {
@@ -142,10 +137,6 @@ const Output = observer(function Output(): React.ReactElement {
142137
143138
*/
144139

145-
const fontSize = outputSettings.fontSize
146-
const scaleVertical = outputSettings.mirrorVertically ? '-1' : '1'
147-
const scaleHorizontal = outputSettings.mirrorHorizontally ? '-1' : '1'
148-
149140
const styleVariables = useMemo(
150141
() =>
151142
({

packages/apps/client/src/views/RundownScript/PreviewPanel.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { observer } from 'mobx-react-lite'
2-
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
2+
import React, { useEffect, useMemo, useRef } from 'react'
33
import { RundownOutput } from 'src/components/RundownOutput/RundownOutput'
44
import { RootAppStore } from 'src/stores/RootAppStore'
55

66
import classes from './PreviewPanel.module.scss'
77
import { useSize } from 'src/lib/useSize'
8+
import { useControllerMessages } from 'src/hooks/useControllerMessages'
89

910
export const PreviewPanel = observer(function PreviewPanel(): React.ReactNode {
1011
const rootEl = useRef<HTMLDivElement>(null)
@@ -19,16 +20,21 @@ export const PreviewPanel = observer(function PreviewPanel(): React.ReactNode {
1920

2021
const viewPortAspectRatio = RootAppStore.viewportStore.viewPort.aspectRatio
2122

23+
const fontSize = outputSettings.fontSize
24+
2225
const size = useSize(rootEl)
2326
const previewWidth = size?.width ?? 0
27+
const previewHeight = size?.height ?? 0
28+
29+
useControllerMessages(rootEl, previewHeight, (previewWidth * fontSize) / 100)
2430

2531
const style = useMemo(
2632
() =>
2733
({
28-
'--prompter-font-size-base': `${(previewWidth * outputSettings.fontSize) / 100}px`,
34+
'--prompter-font-size-base': `${(previewWidth * fontSize) / 100}px`,
2935
height: `${previewWidth / viewPortAspectRatio}px`,
3036
} as React.CSSProperties),
31-
[outputSettings.fontSize, previewWidth, viewPortAspectRatio]
37+
[fontSize, previewWidth, viewPortAspectRatio]
3238
)
3339

3440
return (

0 commit comments

Comments
 (0)