Skip to content

Commit 874e85c

Browse files
committed
fix: RundownView shows spinner when unMOSing a Rundown from a Playlist
1 parent 4b8f2aa commit 874e85c

File tree

2 files changed

+123
-13
lines changed

2 files changed

+123
-13
lines changed

meteor/client/lib/ReactMeteorData/ReactMeteorData.tsx

Lines changed: 112 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable react/prefer-stateless-function */
22

3-
import React, { useState, useEffect, useRef } from 'react'
3+
import React, { useState, useEffect, useRef, useCallback } from 'react'
44
import { Meteor } from 'meteor/meteor'
55
import { Mongo } from 'meteor/mongo'
66
import { Tracker } from 'meteor/tracker'
@@ -13,6 +13,8 @@ const globalTrackerQueue: Array<Function> = []
1313
let globalTrackerTimestamp: number | undefined = undefined
1414
let globalTrackerTimeout: number | undefined = undefined
1515

16+
const SUBSCRIPTION_TIMEOUT = 1000
17+
1618
/**
1719
* Delay an update to be batched with the global tracker invalidation queue
1820
*/
@@ -370,6 +372,46 @@ export function useTracker<T, K extends undefined | T = undefined>(
370372
return meteorData
371373
}
372374

375+
function useReadyState(): {
376+
ready: boolean
377+
setReady: (value: boolean) => void
378+
cancelPreviousReady: (timeout: number) => void
379+
} {
380+
const [ready, setReady] = useState(false)
381+
const [prevReady, setPrevReady] = useState(false)
382+
const prevReadyTimeoutRef = useRef<number | null>(null)
383+
384+
const setIsReady = useCallback(
385+
(value: boolean) => {
386+
setReady(value)
387+
388+
if (value) {
389+
setPrevReady(true)
390+
if (prevReadyTimeoutRef.current !== null) {
391+
window.clearTimeout(prevReadyTimeoutRef.current)
392+
prevReadyTimeoutRef.current = null
393+
}
394+
}
395+
},
396+
[setReady, setPrevReady]
397+
)
398+
399+
const cancelPrevReady = useCallback(
400+
(timeout: number) => {
401+
prevReadyTimeoutRef.current = window.setTimeout(() => {
402+
setPrevReady(false)
403+
}, timeout)
404+
},
405+
[prevReadyTimeoutRef]
406+
)
407+
408+
return {
409+
ready: ready || prevReady,
410+
setReady: setIsReady,
411+
cancelPreviousReady: cancelPrevReady,
412+
}
413+
}
414+
373415
/**
374416
* A Meteor Subscription hook that allows using React Functional Components and the Hooks API with Meteor subscriptions.
375417
* Subscriptions will be torn down 1000ms after unmounting the component.
@@ -383,20 +425,28 @@ export function useSubscription<K extends keyof AllPubSubTypes>(
383425
sub: K,
384426
...args: Parameters<AllPubSubTypes[K]>
385427
): boolean {
386-
const [ready, setReady] = useState<boolean>(false)
428+
const { ready, setReady, cancelPreviousReady } = useReadyState()
387429

388430
useEffect(() => {
389431
const subscription = Tracker.nonreactive(() => meteorSubscribe(sub, ...args))
390-
const isReadyComp = Tracker.nonreactive(() => Tracker.autorun(() => setReady(subscription.ready())))
432+
const isReadyComp = Tracker.nonreactive(() =>
433+
Tracker.autorun(() => {
434+
const isNowReady = subscription.ready()
435+
setReady(isNowReady)
436+
})
437+
)
391438
return () => {
392439
isReadyComp.stop()
393440
setTimeout(() => {
394441
subscription.stop()
395-
}, 1000)
442+
}, SUBSCRIPTION_TIMEOUT)
443+
cancelPreviousReady(SUBSCRIPTION_TIMEOUT)
396444
}
397445
}, [sub, stringifyObjects(args)])
398446

399-
return ready
447+
const isReady = ready
448+
449+
return isReady
400450
}
401451

402452
/**
@@ -415,7 +465,7 @@ export function useSubscriptionIfEnabled<K extends keyof AllPubSubTypes>(
415465
enable: boolean,
416466
...args: Parameters<AllPubSubTypes[K]>
417467
): boolean {
418-
const [ready, setReady] = useState<boolean>(false)
468+
const { ready, setReady, cancelPreviousReady } = useReadyState()
419469

420470
useEffect(() => {
421471
if (!enable) {
@@ -424,16 +474,69 @@ export function useSubscriptionIfEnabled<K extends keyof AllPubSubTypes>(
424474
}
425475

426476
const subscription = Tracker.nonreactive(() => meteorSubscribe(sub, ...args))
427-
const isReadyComp = Tracker.nonreactive(() => Tracker.autorun(() => setReady(subscription.ready())))
477+
const isReadyComp = Tracker.nonreactive(() =>
478+
Tracker.autorun(() => {
479+
const isNowReady = subscription.ready()
480+
setReady(isNowReady)
481+
})
482+
)
428483
return () => {
429484
isReadyComp.stop()
430485
setTimeout(() => {
431486
subscription.stop()
432-
}, 1000)
487+
}, SUBSCRIPTION_TIMEOUT)
488+
cancelPreviousReady(SUBSCRIPTION_TIMEOUT)
433489
}
434490
}, [sub, enable, stringifyObjects(args)])
435491

436-
return !enable || ready
492+
const isReady = !enable || ready
493+
494+
return isReady
495+
}
496+
497+
/**
498+
* A Meteor Subscription hook that allows using React Functional Components and the Hooks API with Meteor subscriptions.
499+
* Subscriptions will be torn down 1000ms after unmounting the component.
500+
* If the subscription is not enabled, the subscription will not be created, and the ready state will always be true.
501+
*
502+
* @export
503+
* @param {PubSub} sub The subscription to be subscribed to
504+
* @param {boolean} enable Whether the subscription is enabled
505+
* @param {...any[]} args A list of arugments for the subscription. This is used for optimizing the subscription across
506+
* renders so that it isn't torn down and created for every render.
507+
*/
508+
export function useSubscriptionIfEnabledReadyOnce<K extends keyof AllPubSubTypes>(
509+
sub: K,
510+
enable: boolean,
511+
...args: Parameters<AllPubSubTypes[K]>
512+
): boolean {
513+
const { ready, setReady, cancelPreviousReady } = useReadyState()
514+
515+
useEffect(() => {
516+
if (!enable) {
517+
setReady(false)
518+
return
519+
}
520+
521+
const subscription = Tracker.nonreactive(() => meteorSubscribe(sub, ...args))
522+
const isReadyComp = Tracker.nonreactive(() =>
523+
Tracker.autorun(() => {
524+
const isNowReady = subscription.ready()
525+
if (isNowReady) setReady(true)
526+
})
527+
)
528+
return () => {
529+
isReadyComp.stop()
530+
setTimeout(() => {
531+
subscription.stop()
532+
}, SUBSCRIPTION_TIMEOUT)
533+
cancelPreviousReady(SUBSCRIPTION_TIMEOUT)
534+
}
535+
}, [sub, enable, stringifyObjects(args)])
536+
537+
const isReady = !enable || ready
538+
539+
return isReady
437540
}
438541

439542
/**

meteor/client/ui/RundownView.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Translated,
88
translateWithTracker,
99
useSubscriptionIfEnabled,
10+
useSubscriptionIfEnabledReadyOnce,
1011
useSubscriptions,
1112
useTracker,
1213
} from '../lib/ReactMeteorData/react-meteor-data'
@@ -1227,9 +1228,6 @@ export function RundownView(props: Readonly<IProps>): JSX.Element {
12271228

12281229
return playlist?.studioId
12291230
}, [playlistId])
1230-
// Load once the playlist is confirmed to exist
1231-
auxSubsReady.push(useSubscriptionIfEnabled(MeteorPubSub.uiSegmentPartNotes, !!playlistStudioId, playlistId))
1232-
auxSubsReady.push(useSubscriptionIfEnabled(MeteorPubSub.uiPieceContentStatuses, !!playlistStudioId, playlistId))
12331231
// Load only when the studio is known
12341232
requiredSubsReady.push(
12351233
useSubscriptionIfEnabled(MeteorPubSub.uiStudio, !!playlistStudioId, playlistStudioId ?? protectString(''))
@@ -1258,7 +1256,12 @@ export function RundownView(props: Readonly<IProps>): JSX.Element {
12581256
)
12591257
)
12601258
requiredSubsReady.push(
1261-
useSubscriptionIfEnabled(CorelibPubSub.showStyleVariants, showStyleVariantIds.length > 0, null, showStyleVariantIds)
1259+
useSubscriptionIfEnabledReadyOnce(
1260+
CorelibPubSub.showStyleVariants,
1261+
showStyleVariantIds.length > 0,
1262+
null,
1263+
showStyleVariantIds
1264+
)
12621265
)
12631266
auxSubsReady.push(
12641267
useSubscriptionIfEnabled(MeteorPubSub.rundownLayouts, showStyleBaseIds.length > 0, showStyleBaseIds)
@@ -1283,6 +1286,10 @@ export function RundownView(props: Readonly<IProps>): JSX.Element {
12831286
)
12841287
)
12851288

1289+
// Load once the playlist is confirmed to exist
1290+
auxSubsReady.push(useSubscriptionIfEnabled(MeteorPubSub.uiSegmentPartNotes, !!playlistStudioId, playlistId))
1291+
auxSubsReady.push(useSubscriptionIfEnabled(MeteorPubSub.uiPieceContentStatuses, !!playlistStudioId, playlistId))
1292+
12861293
useTracker(() => {
12871294
const playlist = RundownPlaylists.findOne(playlistId, {
12881295
fields: {

0 commit comments

Comments
 (0)