Skip to content

Commit 2c2e521

Browse files
committed
Merge pull request #1434 from superFlyTV/feat/simplify-rundownViewEventBus
chore(webui): misc ui refactorings
2 parents 6d8ad57 + 8a50457 commit 2c2e521

File tree

16 files changed

+439
-580
lines changed

16 files changed

+439
-580
lines changed

packages/meteor-lib/src/triggers/RundownViewEventBus.ts

Lines changed: 32 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -127,74 +127,38 @@ export interface ItemDroppedEvent extends IEventContext {
127127
ev: any
128128
}
129129

130-
class RundownViewEventBus0 extends EventEmitter {
131-
emit(event: RundownViewEvents.ACTIVATE_RUNDOWN_PLAYLIST, e: ActivateRundownPlaylistEvent): boolean
132-
emit(event: RundownViewEvents.DEACTIVATE_RUNDOWN_PLAYLIST, e: DeactivateRundownPlaylistEvent): boolean
133-
emit(event: RundownViewEvents.RESYNC_RUNDOWN_PLAYLIST, e: BaseEvent): boolean
134-
emit(event: RundownViewEvents.RESET_RUNDOWN_PLAYLIST, e: BaseEvent): boolean
135-
emit(event: RundownViewEvents.TAKE, e: BaseEvent): boolean
136-
emit(event: RundownViewEvents.REWIND_SEGMENTS): boolean
137-
emit(event: RundownViewEvents.GO_TO_LIVE_SEGMENT): boolean
138-
emit(event: RundownViewEvents.GO_TO_TOP): boolean
139-
emit(event: RundownViewEvents.SEGMENT_ZOOM_ON): boolean
140-
emit(event: RundownViewEvents.SEGMENT_ZOOM_OFF): boolean
141-
emit(event: RundownViewEvents.SHELF_STATE, e: ShelfStateEvent): boolean
142-
emit(event: RundownViewEvents.REVEAL_IN_SHELF, e: RevealInShelfEvent): boolean
143-
emit(event: RundownViewEvents.SWITCH_SHELF_TAB, e: SwitchToShelfTabEvent): boolean
144-
emit(event: RundownViewEvents.MINI_SHELF_QUEUE_ADLIB, e: MiniShelfQueueAdLibEvent): boolean
145-
emit(event: RundownViewEvents.GO_TO_PART, e: GoToPartEvent): boolean
146-
emit(event: RundownViewEvents.GO_TO_PART_INSTANCE, e: GoToPartInstanceEvent): boolean
147-
emit(event: RundownViewEvents.SELECT_PIECE, e: SelectPieceEvent): boolean
148-
emit(event: RundownViewEvents.HIGHLIGHT, e: HighlightEvent): boolean
149-
emit(event: RundownViewEvents.TRIGGER_ACTION, e: TriggerActionEvent): boolean
150-
emit(event: RundownViewEvents.EMPTY_BUCKET, e: BucketEvent): boolean
151-
emit(event: RundownViewEvents.DELETE_BUCKET, e: BucketEvent): boolean
152-
emit(event: RundownViewEvents.RENAME_BUCKET, e: BucketEvent): boolean
153-
emit(event: RundownViewEvents.CREATE_BUCKET, e: IEventContext): boolean
154-
emit(event: RundownViewEvents.DELETE_BUCKET_ADLIB, e: BucketAdLibEvent): boolean
155-
emit(event: RundownViewEvents.RENAME_BUCKET_ADLIB, e: BucketAdLibEvent): boolean
156-
emit(event: RundownViewEvents.CREATE_SNAPSHOT_FOR_DEBUG, e: BaseEvent): boolean
157-
emit(event: RundownViewEvents.TOGGLE_SHELF_DROPZONE, e: ToggleShelfDropzoneEvent): boolean
158-
emit(event: RundownViewEvents.ITEM_DROPPED, e: ItemDroppedEvent): boolean
159-
emit(event: string, ...args: any[]) {
160-
return super.emit(event, ...args)
161-
}
162-
163-
on(event: RundownViewEvents.ACTIVATE_RUNDOWN_PLAYLIST, listener: (e: ActivateRundownPlaylistEvent) => void): this
164-
on(
165-
event: RundownViewEvents.DEACTIVATE_RUNDOWN_PLAYLIST,
166-
listener: (e: DeactivateRundownPlaylistEvent) => void
167-
): this
168-
on(event: RundownViewEvents.RESYNC_RUNDOWN_PLAYLIST, listener: (e: BaseEvent) => void): this
169-
on(event: RundownViewEvents.RESET_RUNDOWN_PLAYLIST, listener: (e: BaseEvent) => void): this
170-
on(event: RundownViewEvents.TAKE, listener: (e: BaseEvent) => void): this
171-
on(event: RundownViewEvents.REWIND_SEGMENTS, listener: () => void): this
172-
on(event: RundownViewEvents.GO_TO_LIVE_SEGMENT, listener: () => void): this
173-
on(event: RundownViewEvents.GO_TO_TOP, listener: () => void): this
174-
on(event: RundownViewEvents.SEGMENT_ZOOM_ON, listener: () => void): this
175-
on(event: RundownViewEvents.SEGMENT_ZOOM_OFF, listener: () => void): this
176-
on(event: RundownViewEvents.REVEAL_IN_SHELF, listener: (e: RevealInShelfEvent) => void): this
177-
on(event: RundownViewEvents.SHELF_STATE, listener: (e: ShelfStateEvent) => void): this
178-
on(event: RundownViewEvents.SWITCH_SHELF_TAB, listener: (e: SwitchToShelfTabEvent) => void): this
179-
on(event: RundownViewEvents.MINI_SHELF_QUEUE_ADLIB, listener: (e: MiniShelfQueueAdLibEvent) => void): this
180-
on(event: RundownViewEvents.GO_TO_PART, listener: (e: GoToPartEvent) => void): this
181-
on(event: RundownViewEvents.GO_TO_PART_INSTANCE, listener: (e: GoToPartInstanceEvent) => void): this
182-
on(event: RundownViewEvents.SELECT_PIECE, listener: (e: SelectPieceEvent) => void): this
183-
on(event: RundownViewEvents.HIGHLIGHT, listener: (e: HighlightEvent) => void): this
184-
on(event: RundownViewEvents.TRIGGER_ACTION, listener: (e: TriggerActionEvent) => void): this
185-
on(event: RundownViewEvents.EMPTY_BUCKET, listener: (e: BucketEvent) => void): this
186-
on(event: RundownViewEvents.DELETE_BUCKET, listener: (e: BucketEvent) => void): this
187-
on(event: RundownViewEvents.RENAME_BUCKET, listener: (e: BucketEvent) => void): this
188-
on(event: RundownViewEvents.CREATE_BUCKET, listener: (e: IEventContext) => void): this
189-
on(event: RundownViewEvents.DELETE_BUCKET_ADLIB, listener: (e: BucketAdLibEvent) => void): this
190-
on(event: RundownViewEvents.RENAME_BUCKET_ADLIB, listener: (e: BucketAdLibEvent) => void): this
191-
on(event: RundownViewEvents.CREATE_SNAPSHOT_FOR_DEBUG, listener: (e: BaseEvent) => void): this
192-
on(event: RundownViewEvents.TOGGLE_SHELF_DROPZONE, listener: (e: ToggleShelfDropzoneEvent) => void): this
193-
on(event: RundownViewEvents.ITEM_DROPPED, listener: (e: ItemDroppedEvent) => void): this
194-
on(event: string, listener: (...args: any[]) => void) {
195-
return super.on(event, listener)
196-
}
197-
}
130+
export interface RundownViewEventBusEvents {
131+
[RundownViewEvents.ACTIVATE_RUNDOWN_PLAYLIST]: [e: ActivateRundownPlaylistEvent]
132+
[RundownViewEvents.DEACTIVATE_RUNDOWN_PLAYLIST]: [e: DeactivateRundownPlaylistEvent]
133+
[RundownViewEvents.RESYNC_RUNDOWN_PLAYLIST]: [e: BaseEvent]
134+
[RundownViewEvents.RESET_RUNDOWN_PLAYLIST]: [e: BaseEvent]
135+
[RundownViewEvents.TAKE]: [e: BaseEvent]
136+
[RundownViewEvents.REWIND_SEGMENTS]: []
137+
[RundownViewEvents.GO_TO_LIVE_SEGMENT]: []
138+
[RundownViewEvents.GO_TO_TOP]: []
139+
[RundownViewEvents.SEGMENT_ZOOM_ON]: []
140+
[RundownViewEvents.SEGMENT_ZOOM_OFF]: []
141+
[RundownViewEvents.SHELF_STATE]: [e: ShelfStateEvent]
142+
[RundownViewEvents.REVEAL_IN_SHELF]: [e: RevealInShelfEvent]
143+
[RundownViewEvents.SWITCH_SHELF_TAB]: [e: SwitchToShelfTabEvent]
144+
[RundownViewEvents.MINI_SHELF_QUEUE_ADLIB]: [e: MiniShelfQueueAdLibEvent]
145+
[RundownViewEvents.GO_TO_PART]: [e: GoToPartEvent]
146+
[RundownViewEvents.GO_TO_PART_INSTANCE]: [e: GoToPartInstanceEvent]
147+
[RundownViewEvents.SELECT_PIECE]: [e: SelectPieceEvent]
148+
[RundownViewEvents.HIGHLIGHT]: [e: HighlightEvent]
149+
[RundownViewEvents.TRIGGER_ACTION]: [e: TriggerActionEvent]
150+
[RundownViewEvents.EMPTY_BUCKET]: [e: BucketEvent]
151+
[RundownViewEvents.DELETE_BUCKET]: [e: BucketEvent]
152+
[RundownViewEvents.RENAME_BUCKET]: [e: BucketEvent]
153+
[RundownViewEvents.CREATE_BUCKET]: [e: IEventContext]
154+
[RundownViewEvents.DELETE_BUCKET_ADLIB]: [e: BucketAdLibEvent]
155+
[RundownViewEvents.RENAME_BUCKET_ADLIB]: [e: BucketAdLibEvent]
156+
[RundownViewEvents.CREATE_SNAPSHOT_FOR_DEBUG]: [e: BaseEvent]
157+
[RundownViewEvents.TOGGLE_SHELF_DROPZONE]: [e: ToggleShelfDropzoneEvent]
158+
[RundownViewEvents.ITEM_DROPPED]: [e: ItemDroppedEvent]
159+
}
160+
161+
class RundownViewEventBus0 extends EventEmitter<RundownViewEventBusEvents> {}
198162

199163
const RundownViewEventBus = new RundownViewEventBus0()
200164
RundownViewEventBus.setMaxListeners(Number.MAX_SAFE_INTEGER)

packages/webui/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
"react-popper": "^2.3.0",
7979
"react-router-bootstrap": "^0.25.0",
8080
"react-router-dom": "^5.3.4",
81-
"react-timer-hoc": "^2.3.0",
8281
"semver": "^7.6.3",
8382
"sha.js": "^2.4.11",
8483
"shuttle-webhid": "^0.0.2",
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import Moment, { MomentProps } from 'react-moment'
22
import moment from 'moment'
3-
import { getCurrentTime } from './systemTime.js'
4-
import timer from 'react-timer-hoc'
3+
import { useCurrentTime } from './lib'
54

65
/**
76
* Use instead of <Moment fromNow></Moment>, its result is synced with getCurrentTime()
87
* @param args same as for Moment
98
*/
10-
export const MomentFromNow = timer(60000)(function MomentFromNow(args: MomentProps) {
11-
return <Moment {...args} from={moment(getCurrentTime())} interval={0}></Moment>
12-
})
9+
export function MomentFromNow(args: MomentProps): JSX.Element {
10+
const time = useCurrentTime(60000)
11+
12+
return <Moment {...args} from={moment(time)} interval={0}></Moment>
13+
}

packages/webui/src/client/lib/lib.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { getCurrentTime, systemTime } from './systemTime.js'
66
import { logger } from './logging.js'
77
import shajs from 'sha.js'
88
import { SINGLE_USE_TOKEN_SALT } from '@sofie-automation/meteor-lib/dist/api/userActions'
9+
import RundownViewEventBus, {
10+
RundownViewEventBusEvents,
11+
RundownViewEvents,
12+
} from '@sofie-automation/meteor-lib/dist/triggers/RundownViewEventBus'
913

1014
export { multilineText, isEventInInputField }
1115

@@ -237,3 +241,16 @@ export function hashSingleUseToken(token: string): string {
237241
.digest('base64')
238242
.replace(/[+/=]/g, '_')
239243
}
244+
245+
export function useRundownViewEventBusListener<TEvent extends RundownViewEvents>(
246+
name: TEvent,
247+
cb: (...args: RundownViewEventBusEvents[TEvent]) => void
248+
): void {
249+
useEffect(() => {
250+
// We need to force the cb type, typescript can't infer this through the generic
251+
RundownViewEventBus.on(name, cb as any)
252+
return () => {
253+
RundownViewEventBus.off(name, cb as any)
254+
}
255+
}, [name, cb])
256+
}

packages/webui/src/client/lib/triggers/TriggersHandler.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ import { IWrappedAdLib } from '@sofie-automation/meteor-lib/dist/triggers/action
1717
import { ReactiveVar } from 'meteor/reactive-var'
1818
import { preventDefault } from '../SorensenContext.js'
1919
import { getFinalKey } from './codesToKeyLabels.js'
20-
import RundownViewEventBus, {
21-
RundownViewEvents,
22-
TriggerActionEvent,
23-
} from '@sofie-automation/meteor-lib/dist/triggers/RundownViewEventBus'
20+
import { RundownViewEvents, TriggerActionEvent } from '@sofie-automation/meteor-lib/dist/triggers/RundownViewEventBus'
2421
import { Tracker } from 'meteor/tracker'
2522
import { Settings } from '../../lib/Settings.js'
2623
import { createInMemorySyncMongoCollection } from '../../collections/lib.js'
@@ -42,7 +39,7 @@ import {
4239
} from '@sofie-automation/meteor-lib/dist/api/MountedTriggers'
4340
import { isHotkeyTrigger } from '@sofie-automation/meteor-lib/dist/triggers/triggerTypeSelectors'
4441
import { RundownPlaylistCollectionUtil } from '../../collections/rundownPlaylistUtil.js'
45-
import { catchError } from '../lib.js'
42+
import { catchError, useRundownViewEventBusListener } from '../lib.js'
4643
import { logger } from '../logging.js'
4744
import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
4845
import { toTriggersComputation, toTriggersReactiveVar, UiTriggersContext } from './triggersContext.js'
@@ -314,12 +311,7 @@ export const TriggersHandler: React.FC<IProps> = function TriggersHandler(
314311
}
315312
}, [initialized]) // run once once Sorensen is initialized
316313

317-
useEffect(() => {
318-
RundownViewEventBus.on(RundownViewEvents.TRIGGER_ACTION, triggerAction)
319-
return () => {
320-
RundownViewEventBus.removeListener(RundownViewEvents.TRIGGER_ACTION, triggerAction)
321-
}
322-
}, [])
314+
useRundownViewEventBusListener(RundownViewEvents.TRIGGER_ACTION, triggerAction)
323315

324316
useEffect(() => {
325317
Tracker.nonreactive(() => {

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

Lines changed: 1 addition & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
} from '../lib/ReactMeteorData/react-meteor-data.js'
1212
import { VTContent, TSR, NoteSeverity, ISourceLayer } from '@sofie-automation/blueprints-integration'
1313
import { useTranslation, withTranslation } from 'react-i18next'
14-
import timer from 'react-timer-hoc'
1514
import * as CoreIcon from '@nrk/core-icons/jsx'
1615
import { Spinner } from '../lib/Spinner.js'
1716
import ClassNames from 'classnames'
@@ -172,117 +171,13 @@ import { PropertiesPanel } from './UserEditOperations/PropertiesPanel.js'
172171
import { PreviewPopUpContextProvider } from './PreviewPopUp/PreviewPopUpContext.js'
173172
import Navbar from 'react-bootstrap/Navbar'
174173
import { AnimatePresence } from 'motion/react'
174+
import { WarningDisplay } from './RundownView/WarningDisplay.js'
175175

176176
const REHEARSAL_MARGIN = 1 * 60 * 1000
177177
const HIDE_NOTIFICATIONS_AFTER_MOUNT: number | undefined = 5000
178178

179179
const DEFAULT_SEGMENT_VIEW_MODE = SegmentViewMode.Timeline
180180

181-
interface ITimingWarningProps {
182-
playlist: DBRundownPlaylist
183-
inActiveRundownView?: boolean
184-
studioMode: boolean
185-
oneMinuteBeforeAction: (e: Event, noResetOnActivate: boolean) => void
186-
}
187-
188-
interface ITimingWarningState {
189-
plannedStartCloseShown?: boolean
190-
plannedStartCloseShow?: boolean
191-
}
192-
const WarningDisplay = withTranslation()(
193-
timer(5000)(
194-
class WarningDisplay extends React.Component<Translated<ITimingWarningProps>, ITimingWarningState> {
195-
constructor(props: Translated<ITimingWarningProps>) {
196-
super(props)
197-
198-
this.state = {}
199-
}
200-
201-
componentDidUpdate(prevProps: ITimingWarningProps) {
202-
if (
203-
(this.props.playlist.activationId && !prevProps.playlist.activationId && this.props.playlist.rehearsal) ||
204-
this.props.playlist.rehearsal !== prevProps.playlist.rehearsal
205-
) {
206-
this.setState({
207-
plannedStartCloseShown: false,
208-
})
209-
}
210-
211-
const expectedStart = PlaylistTiming.getExpectedStart(this.props.playlist.timing)
212-
const expectedDuration = PlaylistTiming.getExpectedDuration(this.props.playlist.timing)
213-
214-
if (
215-
this.props.playlist.activationId &&
216-
this.props.playlist.rehearsal &&
217-
expectedStart &&
218-
// the expectedStart is near
219-
getCurrentTime() + REHEARSAL_MARGIN > expectedStart &&
220-
// but it's not horribly in the past
221-
getCurrentTime() < expectedStart + (expectedDuration || 60 * 60 * 1000) &&
222-
!this.props.inActiveRundownView &&
223-
!this.state.plannedStartCloseShown
224-
) {
225-
this.setState({
226-
plannedStartCloseShow: true,
227-
plannedStartCloseShown: true,
228-
})
229-
}
230-
}
231-
232-
discard = () => {
233-
this.setState({
234-
plannedStartCloseShow: false,
235-
})
236-
}
237-
238-
oneMinuteBeforeAction = (e: any, noResetOnActivate: boolean) => {
239-
this.setState({
240-
plannedStartCloseShow: false,
241-
})
242-
243-
this.props.oneMinuteBeforeAction(e, noResetOnActivate)
244-
}
245-
246-
render(): JSX.Element | null {
247-
const { t } = this.props
248-
249-
if (!this.props.playlist) return null
250-
251-
return (
252-
<ModalDialog
253-
title={t('Start time is close')}
254-
acceptText={t('Reset and Activate "On Air"')}
255-
secondaryText={t('Cancel')}
256-
actions={[
257-
{
258-
label: t('Activate "On Air"'),
259-
classNames: 'btn-secondary',
260-
on: (e) => {
261-
this.oneMinuteBeforeAction(e as Event, true) // this one activates without resetting
262-
},
263-
},
264-
]}
265-
onAccept={(e) => this.oneMinuteBeforeAction(e as Event, false)}
266-
onDiscard={this.discard}
267-
onSecondary={this.discard}
268-
show={
269-
this.props.studioMode &&
270-
this.state.plannedStartCloseShow &&
271-
!(this.props.playlist.activationId && !this.props.playlist.rehearsal) &&
272-
!!this.props.playlist.activationId
273-
}
274-
>
275-
<p>
276-
{t(
277-
'You are in rehearsal mode, the broadcast starts in less than 1 minute. Do you want to go into On-Air mode?'
278-
)}
279-
</p>
280-
</ModalDialog>
281-
)
282-
}
283-
}
284-
)
285-
)
286181
interface ITimingDisplayProps {
287182
rundownPlaylist: DBRundownPlaylist
288183
currentRundown: Rundown | undefined

0 commit comments

Comments
 (0)