From ceede14bea6814dec1ae2e18ab2b7a6af5714f10 Mon Sep 17 00:00:00 2001 From: Johan Nyman Date: Thu, 11 Jan 2024 16:02:56 +0100 Subject: [PATCH] wip --- .../src/api-server/services/PartService.ts | 17 ++- .../src/api-server/services/RundownService.ts | 17 ++- .../src/api-server/services/SegmentService.ts | 13 ++- .../backend/src/data-stores/SegmentStore.ts | 1 + .../BackendPlayground/BackendPlayground.tsx | 51 +++++---- .../client/src/RundownList/RundownEntry.tsx | 2 +- packages/apps/client/src/TestPlaylists.tsx | 6 +- .../client/src/api/ApiConnectionContext.ts | 68 +++++------ .../backendConnection/BackendConnection.ts | 31 +++++ .../apps/client/src/mocks/mockConnection.ts | 11 +- packages/apps/client/src/model/UILine.ts | 17 +-- packages/apps/client/src/model/UIRundown.ts | 108 +++++++++--------- .../apps/client/src/model/UIRundownEntry.ts | 16 +-- packages/apps/client/src/model/UISegment.ts | 73 +++++++----- packages/apps/client/src/stores/AppStore.ts | 17 +-- .../apps/client/src/stores/RundownStore.ts | 25 +++- .../src/client-server-api/RundownService.ts | 2 + 17 files changed, 283 insertions(+), 192 deletions(-) create mode 100644 packages/apps/client/src/backendConnection/BackendConnection.ts diff --git a/packages/apps/backend/src/api-server/services/PartService.ts b/packages/apps/backend/src/api-server/services/PartService.ts index 4db1e51..46971e3 100644 --- a/packages/apps/backend/src/api-server/services/PartService.ts +++ b/packages/apps/backend/src/api-server/services/PartService.ts @@ -73,8 +73,21 @@ export class PartService extends EventEmitter implements Defi } } - public async find(_params?: Params & { paginate?: PaginationParams }): Promise { - return Array.from(this.store.parts.parts.values()) + public async find(params?: Params & { paginate?: PaginationParams }): Promise { + let parts = Array.from(this.store.parts.parts.values()) + if (params?.query?.playlistId) { + const playlistId = params.query.playlistId + parts = parts.filter((p) => p.playlistId === playlistId) + } + if (params?.query?.rundownId) { + const rundownId = params.query.rundownId + parts = parts.filter((p) => p.rundownId === rundownId) + } + if (params?.query?.segmentId) { + const segmentId = params.query.segmentId + parts = parts.filter((p) => p.segmentId === segmentId) + } + return parts } public async get(id: Id, _params?: Params): Promise { const data = this.store.parts.parts.get(id) diff --git a/packages/apps/backend/src/api-server/services/RundownService.ts b/packages/apps/backend/src/api-server/services/RundownService.ts index fd9bdd7..c533cbe 100644 --- a/packages/apps/backend/src/api-server/services/RundownService.ts +++ b/packages/apps/backend/src/api-server/services/RundownService.ts @@ -79,8 +79,15 @@ export class RundownService extends EventEmitter implements D } } - public async find(_params?: Params & { paginate?: PaginationParams }): Promise { - return Array.from(this.store.rundowns.rundowns.values()) + public async find(params?: Params & { paginate?: PaginationParams }): Promise { + // console.log('FIND', _params) + + let rundowns = Array.from(this.store.rundowns.rundowns.values()) + if (params?.query?.playlistId) { + const playlistId = params.query.playlistId + rundowns = rundowns.filter((r) => r.playlistId === playlistId) + } + return rundowns } public async get(id: Id, _params?: Params): Promise { const data = this.store.rundowns.rundowns.get(id) @@ -110,6 +117,12 @@ export class RundownService extends EventEmitter implements D this.app.channel(PublishChannels.RundownsInPlaylist(playlistId)).join(params.connection) this.coreConnection?.subscribeToPlaylist(playlistId) } + public async unSubscribefromRundownsInPlaylist(playlistId: RundownPlaylistId, params: Params): Promise { + if (!params.connection) throw new Error('No connection!') + + this.app.channel(PublishChannels.RundownsInPlaylist(playlistId)).leave(params.connection) + this.coreConnection?.subscribeToPlaylist(playlistId) + } } type Result = Definition.Result type Id = Definition.Id diff --git a/packages/apps/backend/src/api-server/services/SegmentService.ts b/packages/apps/backend/src/api-server/services/SegmentService.ts index e937f8e..8f52a8d 100644 --- a/packages/apps/backend/src/api-server/services/SegmentService.ts +++ b/packages/apps/backend/src/api-server/services/SegmentService.ts @@ -67,8 +67,17 @@ export class SegmentService extends EventEmitter implements D } } - public async find(_params?: Params & { paginate?: PaginationParams }): Promise { - return Array.from(this.store.segments.segments.values()) + public async find(params?: Params & { paginate?: PaginationParams }): Promise { + let segments = Array.from(this.store.segments.segments.values()) + if (params?.query?.playlistId) { + const playlistId = params.query.playlistId + segments = segments.filter((s) => s.playlistId === playlistId) + } + if (params?.query?.rundownId) { + const rundownId = params.query.rundownId + segments = segments.filter((s) => s.rundownId === rundownId) + } + return segments } public async get(id: Id, _params?: Params): Promise { const data = this.store.segments.segments.get(id) diff --git a/packages/apps/backend/src/data-stores/SegmentStore.ts b/packages/apps/backend/src/data-stores/SegmentStore.ts index 8cfc6e6..79647e4 100644 --- a/packages/apps/backend/src/data-stores/SegmentStore.ts +++ b/packages/apps/backend/src/data-stores/SegmentStore.ts @@ -5,6 +5,7 @@ import { Segment, SegmentId } from '@sofie-prompter-editor/shared-model' export class SegmentStore { public readonly segments = observable.map() + public tmp = 0 constructor() { makeObservable(this, { create: action, diff --git a/packages/apps/client/src/BackendPlayground/BackendPlayground.tsx b/packages/apps/client/src/BackendPlayground/BackendPlayground.tsx index 6df741b..4bc8119 100644 --- a/packages/apps/client/src/BackendPlayground/BackendPlayground.tsx +++ b/packages/apps/client/src/BackendPlayground/BackendPlayground.tsx @@ -1,27 +1,30 @@ -import React, { useContext } from 'react' -import { TestInterface } from '../TestInterface' -import { TestPlaylists } from '../TestPlaylists' -import { APIConnectionContext } from '../api/ApiConnectionContext' -import { TestController } from '../TestController.tsx' -import { TestViewPort } from '../TestViewPort.tsx' +import React from 'react' +// import { TestInterface } from '../TestInterface' +// import { TestPlaylists } from '../TestPlaylists' +// import { APIConnectionContext } from '../api/ApiConnectionContext' +// import { TestController } from '../TestController.tsx' +// import { TestViewPort } from '../TestViewPort.tsx' +// import { api } from '../stores/AppStore.ts' -export function BackendPlayground(): React.JSX.Element { - const api = useContext(APIConnectionContext) +export function BackendPlayground(): React.JSX.Element | null { + // const api = useContext(APIConnectionContext) - return ( - <> -
- -
-
- -
-
- -
-
- -
- - ) + return null + + // return ( + // <> + //
+ // + //
+ //
+ // + //
+ //
+ // + //
+ //
+ // + //
+ // + // ) } diff --git a/packages/apps/client/src/RundownList/RundownEntry.tsx b/packages/apps/client/src/RundownList/RundownEntry.tsx index 67510fa..2ace4a8 100644 --- a/packages/apps/client/src/RundownList/RundownEntry.tsx +++ b/packages/apps/client/src/RundownList/RundownEntry.tsx @@ -11,7 +11,7 @@ export const RundownEntry = observer(({ rundownId }: { rundownId: UIRundownId }) const onOpen = () => { if (!rundownEntry) return - navigate(`/rundown/${rundownEntry.playlistId}`) + navigate(`/rundown/${rundownEntry.id}`) } return ( diff --git a/packages/apps/client/src/TestPlaylists.tsx b/packages/apps/client/src/TestPlaylists.tsx index 7d259d9..7d86936 100644 --- a/packages/apps/client/src/TestPlaylists.tsx +++ b/packages/apps/client/src/TestPlaylists.tsx @@ -52,6 +52,9 @@ export const TestPlaylists: React.FC<{ api: APIConnection }> = ({ api }) => { api.playlist.on('updated', (data) => { updatePlaylists(data._id, () => data) }) + api.playlist.on('removed', (id) => { + updatePlaylists(id, () => null) + }) // api.playlist.on('patched', (data) => { // updatePlaylists(data._id, (prev) => { // if (!prev) { @@ -69,9 +72,6 @@ export const TestPlaylists: React.FC<{ api: APIConnection }> = ({ api }) => { // } // }) // }) - api.playlist.on('removed', (id) => { - updatePlaylists(id, () => null) - }) // Also fetch initial list: api.playlist diff --git a/packages/apps/client/src/api/ApiConnectionContext.ts b/packages/apps/client/src/api/ApiConnectionContext.ts index 800e27b..44d7961 100644 --- a/packages/apps/client/src/api/ApiConnectionContext.ts +++ b/packages/apps/client/src/api/ApiConnectionContext.ts @@ -1,38 +1,38 @@ -import React from 'react' -import { APIConnection } from './ApiConnection' -import { assertType } from '@sofie-prompter-editor/shared-lib' -import { RundownPlaylist, RundownPlaylistId } from '@sofie-prompter-editor/shared-model' +// import React from 'react' +// import { APIConnection } from './ApiConnection' +// import { assertType } from '@sofie-prompter-editor/shared-lib' +// import { RundownPlaylist, RundownPlaylistId } from '@sofie-prompter-editor/shared-model' -const api = new APIConnection() -api.on('connected', () => console.log('connected')) -api.on('disconnected', () => console.log('disconnected')) +// const api = new APIConnection() +// api.on('connected', () => console.log('connected')) +// api.on('disconnected', () => console.log('disconnected')) -api.playlist.on('tmpPong', (payload) => { - assertType(payload) - console.log(`Got a tmpPong message: "${payload}"`) -}) -api.playlist.on('created', (payload) => { - assertType(payload) - console.log(`playlist created: "${JSON.stringify(payload)}"`) -}) -// api.playlist.on('patched', (payload) => { -// assertType>(payload) -// console.log(`playlist patched: "${JSON.stringify(payload)}"`) +// api.playlist.on('tmpPong', (payload) => { +// assertType(payload) +// console.log(`Got a tmpPong message: "${payload}"`) +// }) +// api.playlist.on('created', (payload) => { +// assertType(payload) +// console.log(`playlist created: "${JSON.stringify(payload)}"`) +// }) +// // api.playlist.on('patched', (payload) => { +// // assertType>(payload) +// // console.log(`playlist patched: "${JSON.stringify(payload)}"`) +// // }) +// api.playlist.on('updated', (payload) => { +// assertType(payload) +// console.log(`playlist updated: "${JSON.stringify(payload)}"`) +// }) +// api.playlist.on('removed', (id) => { +// assertType(id) +// console.log(`playlist removed: "${id}"`) +// }) +// api.example.on('pongGeneric', (payload) => { +// assertType(payload) +// console.log(`Got a pongGeneric message: "${payload}"`) +// }) +// api.example.on('pongCategory', (message) => { +// console.log(`Got a pongCategory "${message.category}" message: "${message.payload}"`) // }) -api.playlist.on('updated', (payload) => { - assertType(payload) - console.log(`playlist updated: "${JSON.stringify(payload)}"`) -}) -api.playlist.on('removed', (id) => { - assertType(id) - console.log(`playlist removed: "${id}"`) -}) -api.example.on('pongGeneric', (payload) => { - assertType(payload) - console.log(`Got a pongGeneric message: "${payload}"`) -}) -api.example.on('pongCategory', (message) => { - console.log(`Got a pongCategory "${message.category}" message: "${message.payload}"`) -}) -export const APIConnectionContext = React.createContext(api) +// export const APIConnectionContext = React.createContext(api) diff --git a/packages/apps/client/src/backendConnection/BackendConnection.ts b/packages/apps/client/src/backendConnection/BackendConnection.ts new file mode 100644 index 0000000..157de07 --- /dev/null +++ b/packages/apps/client/src/backendConnection/BackendConnection.ts @@ -0,0 +1,31 @@ +import EventEmitter from 'eventemitter3' + +export type BackendConnectionEvents = { + connected: [] + disconnected: [] +} +export class BackendConnection extends EventEmitter { + + public readonly playlist: FeathersTypedService + + constructor() { + super() + } + + // playlist = { + // find: async (args?: Query): Promise => { + // await sleep(500) + // return this._playlists.filter((playlist) => !args || match(playlist, args.query)) + // }, + // get: async (id: RundownPlaylistId): Promise => { + // await sleep(500) + // return this._playlists.find((item) => item._id === id) + // }, + // on: (type: EventTypes, fn: Handler) => { + // this.on(`playlist_${type}`, fn) + // }, + // off: (type: EventTypes, fn: Handler) => { + // this.off(`playlist_${type}`, fn) + // }, + // } +} diff --git a/packages/apps/client/src/mocks/mockConnection.ts b/packages/apps/client/src/mocks/mockConnection.ts index 0a8bff1..58e8e3c 100644 --- a/packages/apps/client/src/mocks/mockConnection.ts +++ b/packages/apps/client/src/mocks/mockConnection.ts @@ -40,7 +40,7 @@ const START_TIME = Date.now() // eslint-disable-next-line @typescript-eslint/no-explicit-any type Handler = (arg: T) => void -type EventTypes = 'created' | 'changed' | 'removed' +type EventTypes = 'created' | 'updated' | 'removed' type Services = 'playlist' | 'rundown' | 'segment' | 'part' type Events = `${Services}_${EventTypes}` | 'connected' | 'disconnected' @@ -84,6 +84,7 @@ export class MockConnection extends EventEmitter { off: (type: EventTypes, fn: Handler) => { this.off(`playlist_${type}`, fn) }, + subscribeToPlaylists: () => {}, } private _rundowns = [ @@ -160,7 +161,7 @@ export class MockConnection extends EventEmitter { }, } - private _parts: Part[] = [ + private _part: Part[] = [ { _id: PART_ID_0_0_0, label: 'Part 0', @@ -329,14 +330,14 @@ export class MockConnection extends EventEmitter { }, ] - parts = { + part = { find: async (args?: Query): Promise => { await sleep(500) - return this._parts.filter((part) => !args || match(part as unknown as { [s: string]: unknown }, args.query)) + return this._part.filter((part) => !args || match(part as unknown as { [s: string]: unknown }, args.query)) }, get: async (id: PartId): Promise => { await sleep(500) - return this._parts.find((item) => item._id === id) + return this._part.find((item) => item._id === id) }, on: (type: EventTypes, fn: Handler) => { this.on(`part_${type}`, fn) diff --git a/packages/apps/client/src/model/UILine.ts b/packages/apps/client/src/model/UILine.ts index 89eabeb..91a875f 100644 --- a/packages/apps/client/src/model/UILine.ts +++ b/packages/apps/client/src/model/UILine.ts @@ -25,25 +25,20 @@ export class UILine { ready: boolean = false - constructor( - private store: RundownStore, - private owner: UISegment, - public partId: PartId, - public id = protectString(randomId()) - ) { + constructor(private store: RundownStore, private owner: UISegment, public id: PartId) { makeAutoObservable(this, { updateFromJson: action, remove: action, }) - this.store.connection.parts.on('changed', (json: Part) => { - if (this.partId !== json._id) return + this.store.connection.part.on('updated', (json: Part) => { + if (this.id !== json._id) return this.updateFromJson(json) }) - this.store.connection.parts.on('removed', (json: Part) => { - if (this.partId !== json._id) return + this.store.connection.part.on('removed', (json) => { + if (this.id !== json._id) return this.remove() }) @@ -72,8 +67,6 @@ export class UILine { dispose() {} } -export type UILineId = ProtectedString<'UILineId', string> - function partDisplayTypeToLineTypeStyle(type: PartDisplayType): LineType { switch (type) { case PartDisplayType.Camera: diff --git a/packages/apps/client/src/model/UIRundown.ts b/packages/apps/client/src/model/UIRundown.ts index 92770e0..a361e7e 100644 --- a/packages/apps/client/src/model/UIRundown.ts +++ b/packages/apps/client/src/model/UIRundown.ts @@ -1,102 +1,89 @@ import { action, computed, makeAutoObservable, observable } from 'mobx' import { - ProtectedString, Rundown, RundownId, RundownPlaylist, RundownPlaylistId, Segment, - protectString, + SegmentId, } from '@sofie-prompter-editor/shared-model' -import { UISegment, UISegmentId } from './UISegment' +import { UISegment } from './UISegment' import { RundownStore } from '../stores/RundownStore' -import { randomId } from '../lib/lib' export class UIRundown { name: string = '' ready: boolean = false - segments = observable.map() + segments = observable.map() private rundowns = observable.map() - constructor( - private store: RundownStore, - public playlistId: RundownPlaylistId, - public id = protectString(randomId()) - ) { + constructor(private store: RundownStore, public id: RundownPlaylistId) { makeAutoObservable(this, { updateFromJson: action, segmentsInOrder: computed, close: action, }) + this.init().catch(console.error) + } + async init() { + await this.store.connection.rundown.subscribeToRundownsInPlaylist(this.id) - this.store.connection.rundown - .find({ - query: { - playlistId: this.playlistId, - }, - }) - .then( - action('receiveRundowns', (rundowns) => { - return Promise.all( - rundowns.map((rundown) => { - this.rundowns.set(rundown._id, rundown) - return this.store.connection.segment.find({ - query: { - rundownId: rundown._id, - }, - }) - }) - ) - }) - ) - .then((segmentArrays) => segmentArrays.flat()) - .then( - action('receiveSegments', (segments) => { - for (const segment of segments) { - const newSegment = new UISegment(this.store, this, segment._id) - this.segments.set(newSegment.id, newSegment) - newSegment.updateFromJson(segment) - } - }) - ) + const rundowns = await this.store.connection.rundown.find({ + query: { + playlistId: this.id, + }, + }) + for (const rundown of rundowns) { + this._onRundownCreated(rundown) + } + + const segments = await this.store.connection.segment.find({ + query: { + playlistId: this.id, + }, + }) + for (const segment of segments) { + this._onSegmentCreated(segment) + } // get all segments // register callbacks for events // we track playlist changed and removed - this.store.connection.playlist.on('changed', (json: RundownPlaylist) => { - if (json._id !== this.playlistId) return + this.store.connection.playlist.on('updated', (json: RundownPlaylist) => { + if (json._id !== this.id) return this.updateFromJson(json) }) this.store.connection.playlist.on('removed', (id: RundownPlaylistId) => { - if (id !== this.playlistId) return + if (id !== this.id) return this.close() }) // we track rundown created, changed and removed, because we own Rundowns - this.store.connection.rundown.on('created', (_json: Rundown) => {}) + this.store.connection.rundown.on('created', (json: Rundown) => { + this.rundowns.set(json._id, json) + }) - this.store.connection.rundown.on('changed', (_json: Rundown) => {}) + this.store.connection.rundown.on('updated', (json: Rundown) => { + this.rundowns.set(json._id, json) + }) - this.store.connection.rundown.on('removed', (id: RundownId) => { - this.rundowns.delete(id) + this.store.connection.rundown.on('removed', (json) => { + this.rundowns.delete(json._id) }) // we track segment created so that we can add new Segments when they are added this.store.connection.segment.on('created', (json: Segment) => { - if (json.playlistId !== this.playlistId) return + if (json.playlistId !== this.id) return if (!this.rundowns.has(json.rundownId)) return - const newSegment = new UISegment(this.store, this, json._id) - this.segments.set(newSegment.id, newSegment) - newSegment.updateFromJson(json) + this._onSegmentCreated(json) }) } @@ -122,6 +109,21 @@ export class UIRundown { dispose(): void { // unregister event handlers from services } -} + private _onRundownCreated = action('onRundownCreated', (json: Rundown) => { + this.rundowns.set(json._id, json) + }) + private _onSegmentCreated = action('onSegmentCreated', (json: Segment) => { + console.log('individual segment', json._id, json) + + const existing = this.segments.get(json._id) + if (!existing) { + const newSegment = new UISegment(this.store, this, json._id) + this.segments.set(json._id, newSegment) + newSegment.updateFromJson(json) + } else { + // update existing segment -export type UIRundownId = ProtectedString<'UIRundownId', string> + existing.updateFromJson(json) + } + }) +} diff --git a/packages/apps/client/src/model/UIRundownEntry.ts b/packages/apps/client/src/model/UIRundownEntry.ts index 041ab88..21df207 100644 --- a/packages/apps/client/src/model/UIRundownEntry.ts +++ b/packages/apps/client/src/model/UIRundownEntry.ts @@ -1,7 +1,5 @@ import { action, makeAutoObservable } from 'mobx' -import { RundownPlaylist, RundownPlaylistId, protectString } from '@sofie-prompter-editor/shared-model' -import { randomId } from '../lib/lib' -import { UIRundownId } from './UIRundown' +import { RundownId, RundownPlaylist, RundownPlaylistId, protectString } from '@sofie-prompter-editor/shared-model' import { RundownStore } from '../stores/RundownStore' // a lightweight domain object for tracking rundowns without their contents @@ -10,25 +8,21 @@ export class UIRundownEntry { ready: boolean = false - constructor( - private store: RundownStore, - public playlistId: RundownPlaylistId, - public id = protectString(randomId()) - ) { + constructor(private store: RundownStore, public id: RundownPlaylistId) { makeAutoObservable(this, { updateFromJson: action, }) void this.store - this.store.connection.playlist.on('changed', (json: RundownPlaylist) => { - if (this.playlistId !== json._id) return + this.store.connection.playlist.on('updated', (json: RundownPlaylist) => { + if (this.id !== json._id) return this.updateFromJson(json) }) this.store.connection.playlist.on('removed', (id: RundownPlaylistId) => { - if (this.playlistId !== id) return + if (this.id !== id) return this.remove() }) diff --git a/packages/apps/client/src/model/UISegment.ts b/packages/apps/client/src/model/UISegment.ts index b58d7ed..0ce766f 100644 --- a/packages/apps/client/src/model/UISegment.ts +++ b/packages/apps/client/src/model/UISegment.ts @@ -1,8 +1,14 @@ import { action, computed, makeAutoObservable, observable } from 'mobx' -import { ProtectedString, RundownId, Segment, SegmentId, protectString } from '@sofie-prompter-editor/shared-model' -import { UILineId, UILine } from './UILine' +import { + PartId, + ProtectedString, + RundownId, + Segment, + SegmentId, + protectString, +} from '@sofie-prompter-editor/shared-model' +import { UILine } from './UILine' import { RundownStore } from '../stores/RundownStore' -import { randomId } from '../lib/lib' import { UIRundown } from './UIRundown' export class UISegment { @@ -14,24 +20,24 @@ export class UISegment { rundownId: RundownId | null = null - lines = observable.map([]) + lines = observable.map([]) - constructor( - private store: RundownStore, - private owner: UIRundown, - public segmentId: SegmentId, - public id = protectString(randomId()) - ) { + // static GetRandomID() { + // return protectString(randomId()) + // } + constructor(private store: RundownStore, private owner: UIRundown, public id: SegmentId) { makeAutoObservable(this, { updateFromJson: action, linesInOrder: computed, remove: action, }) - this.store.connection.parts + console.log(`Created new UISegment: ${id}`, new Error().stack) + + this.store.connection.part .find({ query: { - segmentId: this.segmentId, + segmentId: this.id, }, }) .then( @@ -44,29 +50,40 @@ export class UISegment { }) ) - this.store.connection.segment.on('changed', (json: Segment) => { - if (this.segmentId !== json._id) return + this.store.connection.segment.on( + 'updated', + action('segmentUpdated', (json: Segment) => { + if (this.id !== json._id) return - this.updateFromJson(json) - }) + this.updateFromJson(json) + }) + ) - this.store.connection.segment.on('removed', (json: Segment) => { - if (this.segmentId !== json._id) return + this.store.connection.segment.on( + 'removed', + action('segmentRemoved', (json) => { + if (this.id !== json._id) return - this.remove() - }) + this.remove() + }) + ) // we track segment created so that we can add new Segments when they are added - this.store.connection.parts.on('created', (json) => { - if (json.segmentId !== this.segmentId) return - - const newPart = new UILine(this.store, this, json._id) - this.lines.set(newPart.id, newPart) - newPart.updateFromJson(json) - }) + this.store.connection.part.on( + 'created', + action('createPart', (json) => { + if (json.segmentId !== this.id) return + + const newPart = new UILine(this.store, this, json._id) + this.lines.set(newPart.id, newPart) + newPart.updateFromJson(json) + }) + ) } updateFromJson(json: Segment) { + console.log(`Updated UISegment: ${this.id}`) + this.name = json.label this.rank = json.rank this.rundownId = json.rundownId @@ -89,5 +106,3 @@ export class UISegment { // unregister event handlers } } - -export type UISegmentId = ProtectedString<'UISegmentId', string> diff --git a/packages/apps/client/src/stores/AppStore.ts b/packages/apps/client/src/stores/AppStore.ts index 8594712..ec5b6f9 100644 --- a/packages/apps/client/src/stores/AppStore.ts +++ b/packages/apps/client/src/stores/AppStore.ts @@ -2,26 +2,26 @@ import { makeAutoObservable, action } from 'mobx' import { RundownStore } from './RundownStore' import { MockConnection } from '../mocks/mockConnection' import { UIStore } from './UIStore' - +import { APIConnection as APIConnectionImpl } from '../api/ApiConnection.ts' class AppStoreClass { connected = false rundownStore: RundownStore uiStore: UIStore - connection = new MockConnection() - constructor() { + constructor(public connection: APIConnection) { makeAutoObservable(this) - this.rundownStore = new RundownStore(this, this.connection) + + this.rundownStore = new RundownStore(this, connection) this.uiStore = new UIStore() - this.connection.on( + connection.on( 'connected', action('setConnected', () => { this.connected = true }) ) - this.connection.on( + connection.on( 'disconnected', action('setDisconnected', () => { this.connected = false @@ -29,7 +29,8 @@ class AppStoreClass { ) } } +export const apiConnection = new APIConnectionImpl() -export const AppStore = new AppStoreClass() +export const AppStore = new AppStoreClass(apiConnection) -export type APIConnection = MockConnection +export type APIConnection = APIConnectionImpl diff --git a/packages/apps/client/src/stores/RundownStore.ts b/packages/apps/client/src/stores/RundownStore.ts index 2ff58bb..4a9db9d 100644 --- a/packages/apps/client/src/stores/RundownStore.ts +++ b/packages/apps/client/src/stores/RundownStore.ts @@ -1,28 +1,41 @@ import { makeAutoObservable, observable, action, flow } from 'mobx' import { RundownPlaylistId } from '@sofie-prompter-editor/shared-model' import { APIConnection, AppStore } from './AppStore' -import { UIRundown, UIRundownId } from '../model/UIRundown' +import { UIRundown } from '../model/UIRundown' import { UIRundownEntry } from '../model/UIRundownEntry' export class RundownStore { showingOnlyScripts = false - allRundowns = observable.map() + allRundowns = observable.map() openRundown: UIRundown | null = null constructor(public appStore: typeof AppStore, public connection: APIConnection) { makeAutoObservable(this, { - loadAllRudnowns: action, + loadAllUIRundownData: action, clearAllRundowns: action, loadRundown: action, }) // get all rundowns - this.connection.playlist.on('created', () => {}) - this.loadAllRudnowns() + this.setupUIRundownDataSubscriptions() + this.loadAllUIRundownData() } - loadAllRudnowns = flow(function* (this: RundownStore) { + setupUIRundownDataSubscriptions = flow(function* (this: RundownStore) { + yield this.connection.playlist.subscribeToPlaylists() + + this.connection.playlist.on( + 'created', + action((playlist) => { + const newRundownEntry = new UIRundownEntry(this, playlist._id) + this.allRundowns.set(newRundownEntry.id, newRundownEntry) + newRundownEntry.updateFromJson(playlist) + }) + ) + // 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 diff --git a/packages/shared/model/src/client-server-api/RundownService.ts b/packages/shared/model/src/client-server-api/RundownService.ts index 6dbfa16..99c9fad 100644 --- a/packages/shared/model/src/client-server-api/RundownService.ts +++ b/packages/shared/model/src/client-server-api/RundownService.ts @@ -35,6 +35,8 @@ interface Methods extends Omit { /** Subscribe to all info within a specific playlist */ subscribeToRundownsInPlaylist(playlistId: RundownPlaylistId, params?: Params): Promise + + unSubscribefromRundownsInPlaylist(playlistId: RundownPlaylistId, params?: Params): Promise } export interface Service extends Methods, EventEmitter {}