Skip to content

Commit

Permalink
Merge pull request #1313 from bbc/chore/replace-contextTypes
Browse files Browse the repository at this point in the history
chore: replace react contextTypes with modern alternatives
  • Loading branch information
jstarpl authored Dec 10, 2024
2 parents 162af01 + d798c7c commit fcd04da
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { PropsWithChildren } from 'react'
import { Meteor } from 'meteor/meteor'
import * as PropTypes from 'prop-types'
import { withTracker } from '../../../lib/ReactMeteorData/react-meteor-data'
import { protectString } from '../../../lib/tempLib'
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
Expand All @@ -21,6 +20,7 @@ import { sortPartInstancesInSortedSegments } from '@sofie-automation/corelib/dis
import { RundownUtils } from '../../../lib/rundown'
import { RundownPlaylistClientUtil } from '../../../lib/rundownPlaylistUtil'
import { getCurrentTime } from '../../../lib/systemTime'
import { IRundownTimingProviderValues, RundownTimingProviderContext } from './withTiming'

const TIMING_DEFAULT_REFRESH_INTERVAL = 1000 / 60 // the interval for high-resolution events (timeupdateHR)
const LOW_RESOLUTION_TIMING_DECIMATOR = 15
Expand All @@ -44,10 +44,7 @@ interface IRundownTimingProviderProps {
/** Fallback duration for Parts that have no as-played duration of their own. */
defaultDuration?: number
}
interface IRundownTimingProviderChildContext {
durations: RundownTimingContext
syncedDurations: RundownTimingContext
}

interface IRundownTimingProviderState {}
interface IRundownTimingProviderTrackedProps {
rundowns: Array<Rundown>
Expand Down Expand Up @@ -156,27 +153,28 @@ export const RundownTimingProvider = withTracker<
partsInQuickLoop,
}
})(
class RundownTimingProvider
extends React.Component<
PropsWithChildren<IRundownTimingProviderProps> & IRundownTimingProviderTrackedProps,
IRundownTimingProviderState
>
implements React.ChildContextProvider<IRundownTimingProviderChildContext>
{
static childContextTypes = {
durations: PropTypes.object.isRequired,
syncedDurations: PropTypes.object.isRequired,
}

durations: RundownTimingContext = {
class RundownTimingProvider extends React.Component<
PropsWithChildren<IRundownTimingProviderProps> & IRundownTimingProviderTrackedProps,
IRundownTimingProviderState
> {
private durations: RundownTimingContext = {
isLowResolution: false,
}
syncedDurations: RundownTimingContext = {
private syncedDurations: RundownTimingContext = {
isLowResolution: true,
}
refreshTimer: number | undefined
refreshTimerInterval: number
refreshDecimator: number
/**
* This context works in an unusual way.
* It contains a constant value which gets mutated in place, with the consumer expected to setup a timer to poll for changes.
*/
private childContextValue: IRundownTimingProviderValues = {
durations: this.durations,
syncedDurations: this.syncedDurations,
}

private refreshTimer: number | undefined
private refreshTimerInterval: number
private refreshDecimator: number

private timingCalculator: RundownTimingCalculator = new RundownTimingCalculator()
/** last time (ms rounded down to full seconds) for which the timeupdateSynced event was dispatched */
Expand All @@ -190,18 +188,11 @@ export const RundownTimingProvider = withTracker<
this.refreshDecimator = 0
}

getChildContext(): IRundownTimingProviderChildContext {
return {
durations: this.durations,
syncedDurations: this.syncedDurations,
}
}

calmDownTiming = (time: number) => {
private calmDownTiming = (time: number) => {
return Math.round(time / CURRENT_TIME_GRANULARITY) * CURRENT_TIME_GRANULARITY
}

onRefreshTimer = () => {
private onRefreshTimer = () => {
const now = getCurrentTime()
const calmedDownNow = this.calmDownTiming(now)
this.updateDurations(calmedDownNow, false)
Expand Down Expand Up @@ -252,7 +243,7 @@ export const RundownTimingProvider = withTracker<
if (this.refreshTimer !== undefined) Meteor.clearInterval(this.refreshTimer)
}

dispatchHREvent(now: number) {
private dispatchHREvent(now: number) {
const event = new CustomEvent<TimeEventArgs>(RundownTiming.Events.timeupdateHighResolution, {
detail: {
currentTime: now,
Expand All @@ -262,7 +253,7 @@ export const RundownTimingProvider = withTracker<
window.dispatchEvent(event)
}

dispatchLREvent(now: number) {
private dispatchLREvent(now: number) {
const event = new CustomEvent<TimeEventArgs>(RundownTiming.Events.timeupdateLowResolution, {
detail: {
currentTime: now,
Expand All @@ -272,7 +263,7 @@ export const RundownTimingProvider = withTracker<
window.dispatchEvent(event)
}

dispatchSyncedEvent(now: number) {
private dispatchSyncedEvent(now: number) {
const event = new CustomEvent<TimeEventArgs>(RundownTiming.Events.timeupdateSynced, {
detail: {
currentTime: now,
Expand All @@ -282,7 +273,7 @@ export const RundownTimingProvider = withTracker<
window.dispatchEvent(event)
}

updateDurations(now: number, isSynced: boolean) {
private updateDurations(now: number, isSynced: boolean) {
const { playlist, rundowns, currentRundown, partInstances, partInstancesMap, segmentsMap } = this.props

const updatedDurations = this.timingCalculator.updateDurations(
Expand All @@ -305,7 +296,11 @@ export const RundownTimingProvider = withTracker<
}

render(): React.ReactNode {
return this.props.children
return (
<RundownTimingProviderContext.Provider value={this.childContextValue}>
{this.props.children}
</RundownTimingProviderContext.Provider>
)
}
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react'
import * as PropTypes from 'prop-types'
import * as _ from 'underscore'
import { RundownTiming } from './RundownTiming'
import { RundownTimingContext } from '../../../lib/rundownTiming'
Expand Down Expand Up @@ -32,6 +31,19 @@ type IWrappedComponent<IProps, IState> =
| (new (props: WithTiming<IProps>, state: IState) => React.Component<WithTiming<IProps>, IState>)
| ((props: WithTiming<IProps>) => JSX.Element | null)

export interface IRundownTimingProviderValues {
durations: RundownTimingContext
syncedDurations: RundownTimingContext
}
export const RundownTimingProviderContext = React.createContext<IRundownTimingProviderValues>({
durations: {
isLowResolution: false,
},
syncedDurations: {
isLowResolution: true,
},
})

/**
* Wrap a component in a HOC that will inject a the timing context as a prop. Takes an optional options object that
* allows a high timing resolution or filtering of the changes in the context, so that the child component only
Expand All @@ -58,16 +70,8 @@ export function withTiming<IProps, IState>(

return (WrappedComponent) => {
return class WithTimingHOCComponent extends React.Component<IProps, IState> {
static contextTypes = {
durations: PropTypes.object.isRequired,
syncedDurations: PropTypes.object.isRequired,
}

// Setup by React.Component constructor
declare context: {
durations: RundownTimingContext
syncedDurations: RundownTimingContext
}
static contextType = RundownTimingProviderContext
declare context: React.ContextType<typeof RundownTimingProviderContext>

filterGetter: ((o: any) => any) | undefined
previousValue: any = undefined
Expand Down
13 changes: 3 additions & 10 deletions packages/webui/src/client/ui/SegmentTimeline/SegmentTimeline.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react'
import * as PropTypes from 'prop-types'
import { WithTranslation, withTranslation } from 'react-i18next'

import ClassNames from 'classnames'
Expand Down Expand Up @@ -51,6 +50,7 @@ import {
TimingTickResolution,
TimingDataResolution,
WithTiming,
RundownTimingProviderContext,
} from '../RundownView/RundownTiming/withTiming'
import { SegmentTimeAnchorTime } from '../RundownView/RundownTiming/SegmentTimeAnchorTime'
import { logger } from '../../lib/logging'
Expand Down Expand Up @@ -128,15 +128,8 @@ const SegmentTimelineZoom = class SegmentTimelineZoom extends React.Component<
IProps & IZoomPropsHeader,
IZoomStateHeader
> {
static contextTypes = {
durations: PropTypes.object.isRequired,
}

declare context:
| {
durations: RundownTimingContext
}
| undefined
static contextType = RundownTimingProviderContext
declare context: React.ContextType<typeof RundownTimingProviderContext>

constructor(props: IProps & IZoomPropsHeader, context: any) {
super(props, context)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useMemo } from 'react'
import * as PropTypes from 'prop-types'
import * as _ from 'underscore'
import { SegmentTimeline, SegmentTimelineClass } from './SegmentTimeline'
import { computeSegmentDisplayDuration, RundownTiming, TimingEvent } from '../RundownView/RundownTiming/RundownTiming'
Expand All @@ -24,7 +23,7 @@ import {
ITrackedResolvedSegmentProps,
IOutputLayerUi,
} from '../SegmentContainer/withResolvedSegment'
import { computeSegmentDuration, getPartInstanceTimingId, RundownTimingContext } from '../../lib/rundownTiming'
import { computeSegmentDuration, getPartInstanceTimingId } from '../../lib/rundownTiming'
import { RundownViewShelf } from '../RundownView/RundownViewShelf'
import { PartInstanceId, SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { catchError, useDebounce } from '../../lib/lib'
Expand All @@ -39,6 +38,7 @@ import {
TIMELINE_RIGHT_PADDING,
} from './Constants'
import { UIPartInstances, UIParts } from '../Collections'
import { RundownTimingProviderContext } from '../RundownView/RundownTiming/withTiming'

// Kept for backwards compatibility
export type {
Expand Down Expand Up @@ -141,10 +141,8 @@ export function SegmentTimelineContainer(props: Readonly<IProps>): JSX.Element {

const SegmentTimelineContainerContent = withResolvedSegment(
class SegmentTimelineContainerContent extends React.Component<IProps & ITrackedResolvedSegmentProps, IState> {
static contextTypes = {
durations: PropTypes.object.isRequired,
syncedDurations: PropTypes.object.isRequired,
}
static contextType = RundownTimingProviderContext
declare context: React.ContextType<typeof RundownTimingProviderContext>

isVisible: boolean
rundownCurrentPartInstanceId: PartInstanceId | null = null
Expand All @@ -153,12 +151,6 @@ const SegmentTimelineContainerContent = withResolvedSegment(
mountedTime = 0
nextPartOffset = 0

// Setup by React.Component constructor
declare context: {
durations: RundownTimingContext
syncedDurations: RundownTimingContext
}

constructor(props: IProps & ITrackedResolvedSegmentProps) {
super(props)

Expand Down
14 changes: 3 additions & 11 deletions packages/webui/src/client/ui/SegmentTimeline/TimelineGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'
import _ from 'underscore'
import PropTypes from 'prop-types'

import { RundownUtils } from '../../lib/rundown'

Expand All @@ -10,8 +9,8 @@ import { PartUi } from './SegmentTimelineContainer'
import { getCurrentTime } from '../../lib/systemTime'
import { RundownTiming } from '../RundownView/RundownTiming/RundownTiming'
import { SegmentTimelinePartClass } from './Parts/SegmentTimelinePart'
import { RundownTimingContext } from '../../lib/rundownTiming'
import { PartInstanceId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { RundownTimingProviderContext } from '../RundownView/RundownTiming/withTiming'

// We're cheating a little: Fontface
declare class FontFace {
Expand Down Expand Up @@ -51,15 +50,8 @@ let gridFont: any | undefined = undefined
let gridFontAvailable = false

export class TimelineGrid extends React.Component<ITimelineGridProps> {
static contextTypes = {
durations: PropTypes.object.isRequired,
}

declare context:
| {
durations: RundownTimingContext
}
| undefined
static contextType = RundownTimingProviderContext
declare context: React.ContextType<typeof RundownTimingProviderContext>

canvasElement: HTMLCanvasElement | null = null
parentElement: HTMLDivElement | null = null
Expand Down

0 comments on commit fcd04da

Please sign in to comment.