anear-js-api 0.4.30 → 0.4.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,7 @@ const AnearParticipantMachine = require('../state_machines/AnearParticipantMachi
30
30
  const AnearParticipant = require('../models/AnearParticipant')
31
31
  const RealtimeMessaging = require('../utils/RealtimeMessaging')
32
32
  const MetaProcessing = require('../utils/MetaProcessing')
33
+ const DisplayEventProcessor = require('../utils/DisplayEventProcessor')
33
34
  const PugHelpers = require('../utils/PugHelpers')
34
35
  const C = require('../utils/Constants')
35
36
 
@@ -180,7 +181,7 @@ const ActiveEventStatesConfig = {
180
181
  ],
181
182
  on: {
182
183
  RENDER_DISPLAY: {
183
- target: '.rendering'
184
+ target: '#createdRendering'
184
185
  }
185
186
  },
186
187
  states: {
@@ -195,7 +196,8 @@ const ActiveEventStatesConfig = {
195
196
  }
196
197
  }
197
198
  },
198
- rendering: {
199
+ createdRendering: {
200
+ id: 'createdRendering',
199
201
  deferred: DeferredStatesPlus('ANNOUNCE', 'START'),
200
202
  invoke: {
201
203
  src: 'renderDisplay',
@@ -219,7 +221,7 @@ const ActiveEventStatesConfig = {
219
221
  initial: 'transitioning',
220
222
  on: {
221
223
  RENDER_DISPLAY: {
222
- target: '.rendering'
224
+ target: '#announceRendering'
223
225
  },
224
226
  PARTICIPANT_LEAVE: {
225
227
  // creator browser refresh or MIA. Send disconnect events
@@ -230,19 +232,15 @@ const ActiveEventStatesConfig = {
230
232
  // a participant could re-entering the event after above PARTICIPANT_LEAVE (browser refresh).
231
233
  // so look for existing participants and send reconnect events to interested machines
232
234
  cond: 'participantExists',
233
- actions: 'sendParticipantReconnectEvents',
234
- target: '#waitingToStart',
235
- internal: true
235
+ actions: 'sendParticipantReconnectEvents'
236
236
  },
237
237
  {
238
238
  // spectator clicked JOIN
239
- target: '#newParticipant'
239
+ target: '#spectatorJoining'
240
240
  }
241
241
  ],
242
242
  SPECTATOR_ENTER: {
243
- actions: 'sendSpectatorEnterToAppEventMachine',
244
- target: '#waitingToStart',
245
- internal: true
243
+ actions: 'sendSpectatorEnterToAppEventMachine'
246
244
  }
247
245
  },
248
246
  states: {
@@ -271,7 +269,8 @@ const ActiveEventStatesConfig = {
271
269
  }
272
270
  }
273
271
  },
274
- rendering: {
272
+ announceRendering: {
273
+ id: 'announceRendering',
275
274
  deferred: DeferredStatesPlus('START'),
276
275
  invoke: {
277
276
  src: 'renderDisplay',
@@ -284,30 +283,56 @@ const ActiveEventStatesConfig = {
284
283
  }
285
284
  }
286
285
  },
287
- newParticipant: {
286
+ spectatorJoining: {
288
287
  // a PARTICIPANT_ENTER received from a new user JOIN click. create an AnearParticipantMachine instance.
289
288
  // This machine tracks presence, geo-location (when approved by mobile participant),
290
289
  // manages active/idle state for long-running events, and manages any ACTION timeouts.
291
- id: 'newParticipant',
290
+ id: 'spectatorJoining',
292
291
  deferred: DeferredStatesPlus('START'),
293
292
  invoke: {
294
293
  src: 'fetchParticipantData',
295
294
  onDone: {
296
- actions: [
297
- 'startNewParticipantMachine',
298
- 'sendParticipantEnterToAppEventMachine'
299
- ],
300
- target: '#waitingToStart',
295
+ actions: ['startNewParticipantMachine'],
296
+ target: '#waitParticipantReady'
301
297
  },
302
298
  onError: {
303
299
  target: '#activeEvent.failure'
304
300
  }
305
301
  }
302
+ },
303
+ waitParticipantReady: {
304
+ id: 'waitParticipantReady',
305
+ deferred: DeferredStatesPlus('START'),
306
+ on: {
307
+ PARTICIPANT_MACHINE_READY: {
308
+ actions: ['sendParticipantEnterToAppEventMachine'],
309
+ target: '#waitingToStart',
310
+ internal: true
311
+ }
312
+ }
306
313
  }
307
314
  }
308
315
  },
309
316
  live: {
310
317
  initial: 'transitioning',
318
+ on: {
319
+ RENDER_DISPLAY: {
320
+ target: '#liveRendering'
321
+ },
322
+ SPECTATOR_ENTER: {
323
+ actions: 'sendSpectatorEnterToAppEventMachine'
324
+ },
325
+ PARTICIPANT_LEAVE: {
326
+ // mid-event browser refresh or MIA. Send disconnect events
327
+ actions: ['sendParticipantDisconnectEvents']
328
+ },
329
+ PARTICIPANT_TIMEOUT: {
330
+ // TBD: A participant(s) who was required to take a click action has
331
+ // failed to do so
332
+ },
333
+ PAUSE: '#activeEvent.paused', // Currently no use-case for these
334
+ CLOSE: '#activeEvent.closed'
335
+ },
311
336
  states: {
312
337
  transitioning: {
313
338
  deferred: DeferredStates,
@@ -324,8 +349,72 @@ const ActiveEventStatesConfig = {
324
349
  waitingForActions: {
325
350
  id: 'waitingForActions',
326
351
  on: {
327
- PAUSE: '#activeEvent.paused',
328
- CLOSE: '#activeEvent.closed'
352
+ PARTICIPANT_ENTER: [
353
+ {
354
+ // a participant could re-entering the event after above PARTICIPANT_LEAVE (browser refresh).
355
+ // so look for existing participants and send reconnect events to interested machines
356
+ cond: 'participantExists',
357
+ actions: 'sendParticipantReconnectEvents'
358
+ },
359
+ {
360
+ // in the future, open house events allow spectators to join and leave freely
361
+ // during live.waitingForActions
362
+ cond: 'isOpenHouseEvent',
363
+ target: 'participantEntering'
364
+ },
365
+ {
366
+ // shouldn't receive these so log it and return to current state
367
+ actions: [(c, e) => logger.info("Error: Unexepected PARTICIPANT_ENTER")],
368
+ target: '#activeEvent.failure'
369
+ }
370
+ ],
371
+ ACTION: {
372
+ // A participant has clicked an anear-data-action
373
+ actions: ['processParticipantAction']
374
+ }
375
+ }
376
+ },
377
+ liveRendering: {
378
+ id: 'liveRendering',
379
+ deferred: DeferredStates,
380
+ invoke: {
381
+ src: 'renderDisplay',
382
+ onDone: {
383
+ target: '#waitingForActions',
384
+ internal: true
385
+ },
386
+ onError: {
387
+ target: '#activeEvent.failure'
388
+ }
389
+ }
390
+ },
391
+ participantEntering: {
392
+ // a PARTICIPANT_ENTER received from a new user JOIN click. Unless already exists,
393
+ // create an AnearParticipantMachine instance.
394
+ // This machine tracks presence, geo-location (when approved by mobile participant),
395
+ // manages active/idle state for long-running events, and manages any ACTION timeouts.
396
+ id: 'participantEntering',
397
+ deferred: DeferredStates,
398
+ invoke: {
399
+ src: 'fetchParticipantData',
400
+ onDone: {
401
+ actions: ['startNewParticipantMachine'],
402
+ target: '#waitParticipantJoined',
403
+ },
404
+ onError: {
405
+ target: '#activeEvent.failure'
406
+ }
407
+ }
408
+ },
409
+ waitParticipantJoined: {
410
+ id: 'waitParticipantJoined',
411
+ deferred: DeferredStates,
412
+ on: {
413
+ PARTICIPANT_MACHINE_READY: {
414
+ actions: ['sendParticipantEnterToAppEventMachine'],
415
+ target: '#waitingForActions',
416
+ internal: true
417
+ }
329
418
  }
330
419
  }
331
420
  }
@@ -684,6 +773,9 @@ const AnearEventMachineFunctions = ({
684
773
  sendParticipantEnterToAppEventMachine: (context, event) => {
685
774
  const { anearParticipant } = event.data
686
775
  const participantInfo = context.participants[anearParticipant.id]
776
+
777
+ logger.debug(`Participant machine ${participantInfo.id} is READY`)
778
+
687
779
  const startEvent = { participant: participantInfo }
688
780
 
689
781
  context.appEventMachine.send('PARTICIPANT_ENTER', startEvent)
@@ -790,88 +882,10 @@ const AnearEventMachineFunctions = ({
790
882
  }
791
883
  },
792
884
  services: {
793
- renderDisplay: async (context, event) => {
794
- const processDisplayEvent = displayEvent => {
795
- const { displayType, viewPath, timeout, appExecutionContext } = displayEvent
796
-
797
- logger.debug("processDisplayEvent - displayType: ", displayType, ", viewPath: ", viewPath)
798
-
799
- const renderedDisplayContent = (template, additionalExecutionContext, timeout) => {
800
- const renderedMessage = template(
801
- {
802
- displayType,
803
- ...appExecutionContext,
804
- ...additionalExecutionContext,
805
- participants: context.participants,
806
- ...context.pugHelpers
807
- }
808
- )
809
- const displayContent = { content: renderedMessage }
810
- if (timeout) {
811
- displayContent['timeout'] = timeout
812
- }
813
- return displayContent
814
- }
815
-
816
- const privateParticipantSend = participantMachine => {
817
- const participantState = participantMachine.state
818
- const participantContext = participantState.context
819
- const { appParticipantMachine, anearParticipant } = participantContext
820
-
821
- // TODO: don't send to participants that aren't in an active state
822
-
823
- const appParticipantContext =
824
- appParticipantMachine ? participantContext.appParticipantMachine.state.context : {}
825
-
826
- const privateContext = {
827
- anearParticipant: anearParticipant.participantInfo,
828
- participantContext: appParticipantContext
829
- }
885
+ renderDisplay: (context, event) => {
886
+ const displayEventProcessor = new DisplayEventProcessor(context)
830
887
 
831
- const displayEventPayload = renderedDisplayContent(template, privateContext, timeout)
832
-
833
- participantMachine.send('PRIVATE_DISPLAY', displayEventPayload)
834
- }
835
-
836
- const normalizedPath = viewPath.endsWith(C.PugSuffix) ? viewPath : `${viewPath}${C.PugSuffix}`
837
- const template = context.pugTemplates[normalizedPath]
838
- if (!template) {
839
- throw new Error(`Template not found for path "${normalizedPath}".`)
840
- }
841
- let displayMessage
842
-
843
- switch (displayType) {
844
- case 'participants':
845
- displayMessage = renderedDisplayContent(template, {}, timeout)
846
- return RealtimeMessaging.publish(
847
- context.participantsDisplayChannel,
848
- 'PARTICIPANTS_DISPLAY',
849
- displayMessage.content
850
- )
851
- break
852
- case 'spectators':
853
- displayMessage = renderedDisplayContent(template, {})
854
-
855
- return RealtimeMessaging.publish(
856
- context.spectatorsDisplayChannel,
857
- 'SPECTATORS_DISPLAY',
858
- displayMessage.content
859
- )
860
- break
861
- case 'participant':
862
- // For private displays, iterate over active active participant machines.
863
- Object.values(context.participantMachines).forEach(privateParticipantSend)
864
- return Promise.resolve()
865
- break
866
- default:
867
- throw new Error(`Unknown display type: ${displayType}`)
868
- }
869
- }
870
-
871
- const publishPromises = event.displayEvents.map(
872
- displayEvent => processDisplayEvent(displayEvent)
873
- )
874
- return Promise.all(publishPromises)
888
+ return displayEventProcessor.processAndPublish(event.displayEvents)
875
889
  },
876
890
  getAttachedCreatorOrHost: async (context, event) => {
877
891
  logger.debug("getAttachedCreatorOrHost() invoked")
@@ -937,7 +951,8 @@ const AnearEventMachineFunctions = ({
937
951
  },
938
952
  guards: {
939
953
  participantExists: (context, event) => !!context.participants[event.data.id],
940
- eventCreatorIsHost: (context, event) => context.anearEvent.hosted
954
+ eventCreatorIsHost: (context, event) => context.anearEvent.hosted,
955
+ isOpenHouseEvent: (context, event) => false // TODO: need to have App open_house trait exposed in anear api
941
956
  }
942
957
  })
943
958
 
@@ -22,7 +22,7 @@ const CurrentDateTimestamp = _ => new Date().getTime()
22
22
  const MISSING_TIMEOUT = 10000 // msecs
23
23
 
24
24
  const DeferredStates = [
25
- 'PRIVATE_DISPLAY',
25
+ 'RENDER_DISPLAY',
26
26
  'PARTICIPANT_EXIT',
27
27
  'PARTICIPANT_DISCONNECT',
28
28
  'PARTICIPANT_RECONNECT'
@@ -92,8 +92,8 @@ const AnearParticipantMachineConfig = participantId => ({
92
92
  idle: {
93
93
  id: 'idle',
94
94
  on: {
95
- PRIVATE_DISPLAY: {
96
- target: '#privateDisplay'
95
+ RENDER_DISPLAY: {
96
+ target: '#renderDisplay'
97
97
  },
98
98
  PARTICIPANT_DISCONNECT: {
99
99
  target: 'missing'
@@ -116,11 +116,11 @@ const AnearParticipantMachineConfig = participantId => ({
116
116
  }
117
117
  }
118
118
  },
119
- privateDisplay: {
120
- id: 'privateDisplay',
119
+ renderDisplay: {
120
+ id: 'renderDisplay',
121
121
  deferred: DeferredStates,
122
122
  invoke: {
123
- src: 'privateParticipantDisplay',
123
+ src: 'participantDisplay',
124
124
  onDone: [
125
125
  {
126
126
  cond: 'hasMetaTimeout',
@@ -233,7 +233,7 @@ const AnearParticipantMachineFunctions = {
233
233
  }
234
234
  },
235
235
  services: {
236
- privateParticipantDisplay: async (context, event) => {
236
+ participantDisplay: async (context, event) => {
237
237
  await RealtimeMessaging.publish(
238
238
  context.privateChannel,
239
239
  'PRIVATE_DISPLAY',
@@ -248,7 +248,7 @@ const AnearParticipantMachineFunctions = {
248
248
  },
249
249
  guards: {
250
250
  hasAppParticipantMachine: context => context.appParticipantMachineFactory !== null,
251
- hasMetaTimeout: (context, event) => event.data.timeout.msecs > 0
251
+ hasMetaTimeout: (context, event) => event.data.timeout?.msecs > 0
252
252
  },
253
253
  delays: {
254
254
  timeoutMsecsAfterNoResponse: context => context.noResponseTimeout
@@ -258,7 +258,7 @@ const AnearParticipantMachineFunctions = {
258
258
  // The AnearParticipantMachine:
259
259
  // 1. maintains the presence and geo-location for a Participant in an Event
260
260
  // 2. instantiates the XState Machine return by the (optional) appParticipantMachineFactory
261
- // 3. creates a private display ChannelMachine to which any private display messages get published
261
+ // 3. creates a private display ChannelMachine to which any participant displayType messages get published
262
262
  // 4. handles activity state, response timeouts, idle state
263
263
  // 5. receives ACTION events relayed by the AnearEventMachine
264
264
  // 6. relays all relevant events to the participant XState Machine for Application-specific handling
@@ -0,0 +1,129 @@
1
+ "use strict"
2
+ /**
3
+ * --------------------------------------------------------------------------
4
+ * Pug templates receive the following context at render:
5
+ *
6
+ * {
7
+ * app: application XState context
8
+ * meta: {
9
+ * viewer: // 'participant', 'participants', 'spectators'
10
+ * state: // Stringified state name (e.g., 'live.registration.waitForOpponentToJoin')
11
+ * event: // The triggering event for this transition ('PARTICIPANT_ENTER')
12
+ * timeout: // null|| { msecs:, [participantId:]}
13
+ * }
14
+ *
15
+ * // When 'participant' displayType
16
+ * participants: // all: Map of all participants [info, context] for this event, get(id) => info, context
17
+ * participant: // info, context
18
+ *
19
+ * // PLUS all helpers from PugHelpers:
20
+ * cdnImg(filename) // Resolves CDN image path
21
+ * action(payload) // JSON-encodes click action payloads for data attributes
22
+ * }
23
+ *
24
+ * Example usage in Pug:
25
+ *
26
+ * img(src=cdnImg('logo.png'))
27
+ * .sq(data-anear-click=action({MOVE: {x, y}}))
28
+ * each participant in Object.values(participants)
29
+ * li= participant.name
30
+ *
31
+ * --------------------------------------------------------------------------
32
+ */
33
+
34
+ const logger = require('./Logger')
35
+ const RealtimeMessaging = require('./RealtimeMessaging')
36
+ const C = require('./Constants')
37
+
38
+ class DisplayEventProcessor {
39
+ constructor(anearEventMachineContext) {
40
+ this.pugTemplates = anearEventMachineContext.pugTemplates
41
+ this.pugHelpers = anearEventMachineContext.pugHelpers
42
+ this.participantMachines = anearEventMachineContext.participantMachines
43
+ this.participantsDisplayChannel = anearEventMachineContext.participantsDisplayChannel
44
+ this.spectatorsDisplayChannel = anearEventMachineContext.spectatorsDisplayChannel
45
+ this.participantsIndex = this._buildParticipantsIndex(anearEventMachineContext.participants)
46
+ }
47
+
48
+ processAndPublish(displayEvents) {
49
+ return Promise.all(displayEvents.map(event => this._processSingle(event)))
50
+ }
51
+
52
+ _buildParticipantsIndex(participants) {
53
+ const participantStructs = Object.fromEntries(
54
+ Object.entries(participants).map(([id, info]) => [ id, { info, context: null } ])
55
+ )
56
+ const all = Object.values(participantStructs)
57
+
58
+ return { all, get: id => participantStructs[id] }
59
+ }
60
+
61
+ _processSingle(displayEvent) {
62
+ const { viewPath, appRenderContext } = displayEvent
63
+ const timeout = appRenderContext.meta.timeout
64
+
65
+ const normalizedPath = viewPath.endsWith(C.PugSuffix) ? viewPath : `${viewPath}${C.PugSuffix}`
66
+ const template = this.pugTemplates[normalizedPath]
67
+
68
+ if (!template) {
69
+ throw new Error(`Template not found: ${normalizedPath}`)
70
+ }
71
+
72
+ const templateRenderContext = {
73
+ ...appRenderContext,
74
+ participants: this.participantsIndex,
75
+ ...this.pugHelpers
76
+ }
77
+
78
+ switch (appRenderContext.meta.viewer) {
79
+ case 'participants':
80
+ logger.debug(`[DisplayEventProcessor] Publishing PARTICIPANTS_DISPLAY`)
81
+
82
+ return RealtimeMessaging.publish(
83
+ this.participantsDisplayChannel,
84
+ 'PARTICIPANTS_DISPLAY',
85
+ template(templateRenderContext)
86
+ )
87
+
88
+ case 'spectators':
89
+ logger.debug(`[DisplayEventProcessor] Publishing SPECTATORS_DISPLAY`)
90
+
91
+ return RealtimeMessaging.publish(
92
+ this.spectatorsDisplayChannel,
93
+ 'SPECTATORS_DISPLAY',
94
+ template(templateRenderContext)
95
+ )
96
+
97
+ case 'participant':
98
+ logger.debug(`[DisplayEventProcessor] Processing RENDER_DISPLAY for each participant`)
99
+ return this._processPrivateParticipantDisplays(template, templateRenderContext, timeout)
100
+
101
+ default:
102
+ throw new Error(`Unknown meta.viewer: ${appRenderContext.meta.viewer}`)
103
+ }
104
+ }
105
+
106
+ _processPrivateParticipantDisplays(template, templateRenderContext, timeout) {
107
+ Object.values(this.participantsIndex.all).forEach(
108
+ participantStruct => {
109
+ const participantId = participantStruct.info.id
110
+ const participantMachine = this.participantMachines[participantId]
111
+ const privateRenderContext = {
112
+ ...templateRenderContext,
113
+ participant: participantStruct
114
+ }
115
+
116
+ const privateHtml = template(privateRenderContext)
117
+
118
+ const renderMessage = { content: privateHtml }
119
+ if (timeout) renderMessage.timeout = timeout
120
+
121
+ participantMachine.send('RENDER_DISPLAY', renderMessage)
122
+ }
123
+ )
124
+
125
+ return Promise.resolve()
126
+ }
127
+ }
128
+
129
+ module.exports = DisplayEventProcessor
@@ -2,146 +2,97 @@
2
2
 
3
3
  const logger = require('./Logger')
4
4
 
5
- // Helper functions for processing meta configurations
6
- const buildTimeout = (timeoutType, timeoutConfig, appExecutionContext) => {
7
- let msecs = null
8
- let participantId = null
9
-
10
- switch (typeof timeoutConfig) {
11
- case 'function':
12
- [msecs, participantId] = timeoutConfig(appExecutionContext)
13
- break
14
- case 'number':
15
- msecs = timeoutConfig
16
- break
17
- default:
18
- throw new Error(`unknown timeout config ${typeof timeoutConfig}`)
19
- }
20
-
21
- switch (timeoutType) {
22
- case 'participant':
23
- return { type: timeoutType, msecs, participantId }
24
- case 'participants':
25
- return { type: timeoutType, msecs }
26
- default:
27
- throw new Error(`unknown timeout type ${timeoutType}`)
28
- }
29
- }
30
-
31
- const metaConfig = (meta, timeoutType, appExecutionContext) => {
32
- let timeout = {msecs: 0}
33
- let view = null
34
-
35
- switch (typeof meta) {
36
- case 'string':
37
- view = meta
38
- break
39
- case 'object':
40
- view = meta.view
41
- if (meta.timeout) {
42
- timeout = buildTimeout(timeoutType, meta.timeout, appExecutionContext)
5
+ /**
6
+ * MetaProcessing:
7
+ * - Runs inside your appEventMachine onTransition.
8
+ * - Knows how to parse the XState meta shape (participants, participant, spectators).
9
+ * - Normalizes viewPath + timeout.
10
+ * - Emits displayEvents → sends to AnearEventMachine for rendering.
11
+ */
12
+ const MetaProcessing = (anearEvent) => {
13
+ return (appEventMachineState) => {
14
+ const { meta: rawMeta, context: appContext, value, event } = appEventMachineState
15
+ const [meta] = Object.values(rawMeta)
16
+
17
+ if (!meta) return
18
+
19
+ const appStateName = _stringifiedState(value)
20
+
21
+ const appRenderContext = (displayType, timeout = null) => (
22
+ {
23
+ app: appContext,
24
+ meta: {
25
+ state: appStateName,
26
+ event,
27
+ timeout,
28
+ viewer: displayType
29
+ }
43
30
  }
31
+ )
44
32
 
45
- break
46
- default:
47
- throw new Error(`unknown ${timeoutType} meta format ${meta}`)
48
- }
49
- return [view, timeout]
50
- }
51
-
52
- const participantsMetaConfig = (participantsMeta, appExecutionContext) => {
53
- return metaConfig(participantsMeta, 'participants', appExecutionContext)
54
- }
55
-
56
- const participantMetaConfig = (participantMeta, appExecutionContext) => {
57
- return metaConfig(participantMeta, 'participant', appExecutionContext)
58
- }
59
-
60
- const spectatorMetaConfig = config => {
61
- if (typeof config === 'string')
62
- return config
63
- else if (typeof config === 'object')
64
- return config.view
65
- else
66
- throw new Error(`unknown spectator meta format ${config}`)
67
- }
68
-
69
- const Stringified = (stateValue) => {
70
- switch (typeof stateValue) {
71
- case 'string':
72
- return stateValue
73
- case 'object':
74
- return Object.entries(stateValue)
75
- .map(([key, nested]) => `${key}.${Stringified(nested)}`)
76
- .join('.')
77
- default:
78
- throw new Error(`unknown Xstate state value type ${typeof stateValue}`)
79
- }
80
- }
81
-
82
- const MetaProcessing = anearEvent => {
83
- return appEventMachineState => {
84
- const { context: appContext, meta: rawMeta } = appEventMachineState
85
- const appStateName = Stringified(appEventMachineState.value)
86
33
  const displayEvents = []
87
34
 
88
- //logger.debug("MetaProcessing invoked on transition to: ", appStateName)
89
- //logger.debug("stateMeta: ", appEventMachineState.meta)
90
-
91
- if (Object.keys(appEventMachineState.meta).length === 0) {
92
- return
35
+ if (meta.participants) {
36
+ const viewPath = _getViewPath(meta.participants)
37
+ const timeout = _buildTimeout(meta.participants.timeout, appContext)
38
+ const displayEvent = {
39
+ viewPath,
40
+ appRenderContext: appRenderContext('participants', timeout)
41
+ }
42
+ displayEvents.push(displayEvent)
93
43
  }
94
44
 
95
- logger.debug("App Context: ", appContext)
96
-
97
- const sendDisplayMessage = () => {
98
- logger.debug(`${appStateName} 'RENDER_DISPLAY' to ${anearEvent.id} with ${displayEvents.length} msgs`)
99
-
100
- // sends a RENDER_DISPLAY event with one or more viewPaths and appExecutionContext in the displayEvents array
101
- anearEvent.send('RENDER_DISPLAY', { displayEvents })
45
+ if (meta.participant) {
46
+ const viewPath = _getViewPath(meta.participant)
47
+ const timeout = _buildTimeout(meta.participant.timeout, appContext)
48
+ const displayEvent = {
49
+ viewPath,
50
+ appRenderContext: appRenderContext('participant', timeout)
51
+ }
52
+ displayEvents.push(displayEvent)
102
53
  }
103
54
 
104
- const appendDisplayEvent = (displayType, viewPath, timeout = null) => {
105
- displayEvents.push(
106
- {
107
- displayType,
108
- viewPath,
109
- timeout,
110
- appExecutionContext
111
- }
112
- )
55
+ if (meta.spectators) {
56
+ const viewPath = _getViewPath(meta.spectators)
57
+ const displayEvent = {
58
+ viewPath,
59
+ appRenderContext: appRenderContext('spectators')
60
+ }
61
+ displayEvents.push(displayEvent)
113
62
  }
114
63
 
115
- const appExecutionContext = {
116
- context: appContext,
117
- state: appStateName,
118
- event: appEventMachineState.event
64
+ if (displayEvents.length > 0) {
65
+ logger.debug(`[MetaProcessing] sending RENDER_DISPLAY with ${displayEvents.length} displayEvents`)
66
+ anearEvent.send('RENDER_DISPLAY', { displayEvents })
119
67
  }
68
+ }
69
+ }
120
70
 
121
- const [meta] = Object.values(appEventMachineState.meta)
122
-
123
- logger.debug("meta: ", meta)
71
+ const _getViewPath = (config) => {
72
+ if (!config) return null
73
+ if (typeof config === 'string') return config
74
+ if (typeof config === 'object') return config.view
75
+ throw new Error(`Unknown meta format: ${JSON.stringify(config)}`)
76
+ }
124
77
 
125
- // Only proceed if there's at least one display meta defined.
126
- if (!meta.participants && !meta.participant && !meta.spectators) return
78
+ const _buildTimeout = (timeoutConfig, appContext) => {
79
+ if (!timeoutConfig) return null
127
80
 
128
- if (meta.participants) {
129
- const [viewPath, timeout] = participantsMetaConfig(meta.participants, appExecutionContext)
130
- appendDisplayEvent('participants', viewPath, timeout)
131
- }
81
+ if (typeof timeoutConfig === 'function') {
82
+ const [msecs, participantId] = timeoutConfig(appContext)
83
+ return { msecs, participantId }
84
+ }
132
85
 
133
- if (meta.participant) {
134
- const [viewPath, timeout] = participantMetaConfig(meta.participant, appExecutionContext)
135
- appendDisplayEvent('participant', viewPath, timeout)
136
- }
86
+ if (typeof timeoutConfig === 'number') {
87
+ return { msecs: timeoutConfig }
88
+ }
137
89
 
138
- if (meta.spectators) {
139
- const viewPath = spectatorMetaConfig(meta.spectators)
140
- appendDisplayEvent('spectators', viewPath)
141
- }
90
+ throw new Error(`Unknown timeout config: ${typeof timeoutConfig}`)
91
+ }
142
92
 
143
- sendDisplayMessage()
144
- }
93
+ const _stringifiedState = (stateValue) => {
94
+ if (typeof stateValue === 'string') return stateValue
95
+ return Object.entries(stateValue).map(([k, v]) => `${k}.${_stringifiedState(v)}`).join('.')
145
96
  }
146
97
 
147
98
  module.exports = MetaProcessing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anear-js-api",
3
- "version": "0.4.30",
3
+ "version": "0.4.33",
4
4
  "description": "Javascript Developer API for Anear Apps",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {