Skip to content

Commit

Permalink
fix: make subscriptions reactive to connection status
Browse files Browse the repository at this point in the history
restore subscriptions after re-connecting
  • Loading branch information
jstarpl committed Jan 16, 2024
1 parent 170669e commit 79d0064
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import EventEmitter from 'eventemitter3'
import { Application, PaginationParams, Params, Query } from '@feathersjs/feathers'
import { Application, PaginationParams, Params } from '@feathersjs/feathers'
import {
ServiceTypes,
Services,
Expand Down
112 changes: 60 additions & 52 deletions packages/apps/client/src/model/UIRundown.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { action, computed, makeAutoObservable, observable } from 'mobx'
import { IReactionDisposer, action, computed, flow, makeObservable, observable, reaction, when } from 'mobx'
import {
Rundown,
RundownId,
Expand All @@ -21,18 +21,31 @@ export class UIRundown {

private rundowns = observable.map<RundownId, Rundown>()

reactions: IReactionDisposer[] = []

constructor(private store: RundownStore, public id: UIRundownId) {
makeAutoObservable(this, {
updateFromJson: action,
makeObservable(this, {
segmentsInOrder: computed,
close: action,
})
this.init().catch(console.error)
}
async init() {
await this.store.connection.rundown.subscribeToRundownsInPlaylist(this.id)
init = flow(function* (this: UIRundown) {
this.reactions.push(
reaction(
() => this.store.appStore.connected,
async (connected) => {
if (!connected) return

await this.store.connection.rundown.subscribeToRundownsInPlaylist(this.id)
},
{
fireImmediately: true,
}
)
)

const rundowns = await this.store.connection.rundown.find({
const rundowns = yield this.store.connection.rundown.find({
query: {
playlistId: this.id,
},
Expand All @@ -41,7 +54,7 @@ export class UIRundown {
this.onRundownCreated(rundown)
}

const segments = await this.store.connection.segment.find({
const segments = yield this.store.connection.segment.find({
query: {
playlistId: this.id,
},
Expand All @@ -55,54 +68,22 @@ export class UIRundown {
// register callbacks for events

// we track playlist changed and removed
this.store.connection.playlist.on(
'updated',
action((json: RundownPlaylist) => {
if (json._id !== this.id) return

this.updateFromJson(json)
})
)

this.store.connection.playlist.on(
'removed',
action((id: RundownPlaylistId) => {
if (id !== this.id) return

this.close()
})
)
this.store.connection.playlist.on('updated', this.onPlaylistUpdated)
this.store.connection.playlist.on('removed', this.onPlaylistRemoved)

// we track rundown created, changed and removed, because we own Rundowns
this.store.connection.rundown.on(
'created',
action((json: Rundown) => {
this.rundowns.set(json._id, json)
})
)

this.store.connection.rundown.on(
'updated',
action((json: Rundown) => {
this.rundowns.set(json._id, json)
})
)

this.store.connection.rundown.on(
'removed',
action((json) => {
this.rundowns.delete(json._id)
})
)
this.store.connection.rundown.on('created', this.onRundownCreated)
this.store.connection.rundown.on('updated', this.onRundownUpdated)
this.store.connection.rundown.on('removed', this.onRundownRemoved)

// we track segment created so that we can add new Segments when they are added
this.store.connection.segment.on('created', this.onSegmentCreated)
}
})

updateFromJson(json: RundownPlaylist) {
updateFromJson = action('updateFromJson', (json: RundownPlaylist) => {
this.name = json.label
this.ready = true
}
})

get segmentsInOrder(): UISegment[] {
return Array.from(this.segments.values()).sort((a, b) => {
Expand All @@ -113,22 +94,49 @@ export class UIRundown {
})
}

close(): void {
close = action('close', () => {
this.store.openRundown = null

this.store.connection.rundown.unSubscribeFromRundownsInPlaylist(this.id).catch(console.error)
this.dispose()
}
})

dispose(): void {
// unregister event handlers from services
this.store.connection.segment.off('created', this.onSegmentCreated)
this.reactions.forEach((destroy) => destroy())

this.store.connection.playlist.off('updated', this.onPlaylistUpdated)
this.store.connection.playlist.off('removed', this.onPlaylistRemoved)

// TODO: Add more handlers
this.store.connection.rundown.off('created', this.onRundownCreated)
this.store.connection.rundown.off('updated', this.onRundownUpdated)
this.store.connection.rundown.off('removed', this.onRundownRemoved)

this.store.connection.segment.off('created', this.onSegmentCreated)
}

private onPlaylistUpdated = action('onPlaylistUpdated', (json: RundownPlaylist) => {
if (json._id !== this.id) return

this.updateFromJson(json)
})
private onPlaylistRemoved = action('onPlaylistRemoved', (id: RundownPlaylistId) => {
if (id !== this.id) return

this.close()
})
private onRundownCreated = action('onRundownCreated', (json: Rundown) => {
if (json.playlistId !== this.id) return

this.rundowns.set(json._id, json)
})
private onRundownUpdated = action('onRundownUpdated', (json: Rundown) => {
if (json.playlistId !== this.id) return

this.rundowns.set(json._id, json)
})
private onRundownRemoved = action('onRundownRemoved', (json: Pick<Rundown, '_id'>) => {
this.rundowns.delete(json._id)
})
private onSegmentCreated = action('onSegmentCreated', (json: Segment) => {
if (json.playlistId !== this.id) return
if (!this.rundowns.has(json.rundownId)) return
Expand Down
18 changes: 9 additions & 9 deletions packages/apps/client/src/stores/AppStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,28 @@ class AppStoreClass {
uiStore: UIStore

constructor() {
makeObservable(this, {
connected: observable,
})

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const apiConnection = USE_MOCK_CONNECTION ? (new MockConnection() as any) : new APIConnectionImpl()
this.connection = apiConnection
this.rundownStore = new RundownStore(this, this.connection)
this.uiStore = new UIStore()

makeObservable(this, {
connected: observable,
rundownStore: observable,
uiStore: observable,
})

this.connection.on('connected', this.onDisconnected)
this.connection.on('disconnected', this.onDisconnected)

this.connection.on('disconnected', this.onConnected)
this.connection.on('connected', this.onConnected)
}

onConnected = action('onConnected', () => {
console.log('Connected')
this.connected = true
})

onDisconnected = action('onConnected', () => {
onDisconnected = action('onDisconnected', () => {
console.log('Disconnected')
this.connected = false
})
}
Expand Down
37 changes: 28 additions & 9 deletions packages/apps/client/src/stores/RundownStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { makeAutoObservable, observable, action, flow } from 'mobx'
import { observable, action, flow, makeObservable, IReactionDisposer, reaction } from 'mobx'
import { RundownPlaylistId } from '@sofie-prompter-editor/shared-model'
import { APIConnection, AppStore } from './AppStore'
import { UIRundown } from '../model/UIRundown'
Expand All @@ -10,20 +10,34 @@ export class RundownStore {
allRundowns = observable.map<RundownPlaylistId, UIRundownEntry>()
openRundown: UIRundown | null = null

reactions: IReactionDisposer[] = []

constructor(public appStore: typeof AppStore, public connection: APIConnection) {
makeAutoObservable(this, {
loadAllUIRundownData: action,
clearAllRundowns: action,
loadRundown: action,
makeObservable(this, {
openRundown: observable,
showingOnlyScripts: observable,
})

// get all rundowns
this.setupUIRundownDataSubscriptions()
this.loadAllUIRundownData()
}

setupUIRundownDataSubscriptions = flow(function* (this: RundownStore) {
yield this.connection.playlist.subscribeToPlaylists()
setupUIRundownDataSubscriptions = action(() => {
this.reactions.push(
reaction(
() => this.appStore.connected,
async (connected) => {
console.log('Connected is: ', connected)
if (!connected) return

await this.connection.playlist.subscribeToPlaylists()
},
{
fireImmediately: true,
}
)
)

this.connection.playlist.on(
'created',
Expand All @@ -35,6 +49,7 @@ export class RundownStore {
)
// Note: updated and removed events are handled by the UIRundownEntry's themselves
})

loadAllUIRundownData = flow(function* (this: RundownStore) {
const playlists = yield this.connection.playlist.find()
// add UIRundownEntries to allRundowns
Expand All @@ -48,11 +63,11 @@ export class RundownStore {
}
})

clearAllRundowns() {
clearAllRundowns = action('clearAllRundowns', () => {
for (const rundown of this.allRundowns.values()) {
rundown.remove()
}
}
})

loadRundown = flow(function* (this: RundownStore, id: RundownPlaylistId) {
this.openRundown?.close()
Expand All @@ -67,4 +82,8 @@ export class RundownStore {
newRundown.updateFromJson(playlist)
this.openRundown = newRundown
})

destroy = () => {
this.reactions.forEach((dispose) => dispose())
}
}

0 comments on commit 79d0064

Please sign in to comment.