anear-js-api 0.4.7 → 0.4.8

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.
@@ -137,7 +137,6 @@ class AnearMessaging {
137
137
 
138
138
  if (!eventExists) {
139
139
  const messagingInitializedCallback = async () => {
140
- await anearEvent.createdEventCallback()
141
140
  await anearEvent.persist()
142
141
  // start the state machine before initializing Realtime Messaging
143
142
  // as REFRESH events come in and the state machine should be ready
@@ -214,7 +213,7 @@ class AnearMessaging {
214
213
  this.subscribePresenceEvent(
215
214
  spectatorsChannel,
216
215
  PRESENCE_ENTER,
217
- async message => await this.spectatorEnterMessagingCallback(anearEvent, message)
216
+ async message => await this.spectatorViewMessagingCallback(anearEvent, message)
218
217
  )
219
218
 
220
219
  this.subscribePresenceEvent(
@@ -351,12 +350,11 @@ class AnearMessaging {
351
350
  })
352
351
  }
353
352
 
354
- async spectatorEnterMessagingCallback(anearEvent, message) {
353
+ spectatorViewMessagingCallback(anearEvent, message) {
355
354
  const userId = message.clientId
355
+ logger.debug(`**** ENTER SPECTATOR FOR VIEWING **** event: ${anearEvent.id}, user: ${userId}`)
356
356
 
357
- logger.debug(`**** ENTER SPECTATOR **** event: ${anearEvent.id}, user: ${userId}`)
358
-
359
- await anearEvent.refreshSpectator()
357
+ anearEvent.spectatorView(userId)
360
358
  }
361
359
 
362
360
  async spectatorLeaveMessagingCallback(anearEvent, message) {
@@ -450,8 +448,8 @@ class AnearMessaging {
450
448
  logger.debug(`setupPrivatePublishingChannel(${participant.privateChannelName}) state ${privateChannel.state}`)
451
449
  }
452
450
 
453
- async attachChannel(channel) {
454
- return await channel.attach()
451
+ attachChannel(channel) {
452
+ return channel.attach()
455
453
  }
456
454
 
457
455
  subscribeEventMessages(channel, messageType, callback) {
@@ -3,7 +3,6 @@
3
3
  const { Mutex } = require('async-mutex')
4
4
 
5
5
  const AnearXstate = require('../utils/AnearXstate')
6
- const { DefaultConfigFunc, DefaultOptionsFunc } = require('../utils/AnearXstateDefaults')
7
6
 
8
7
  const JsonApiResource = require('./JsonApiResource')
9
8
  const Participants = require('../utils/Participants')
@@ -40,12 +39,12 @@ class AnearEvent extends JsonApiResource {
40
39
 
41
40
  stateMachineConfig() {
42
41
  // override in subclass with custom Xstate config
43
- return DefaultConfigFunc(this)
42
+ return {}
44
43
  }
45
44
 
46
45
  stateMachineOptions() {
47
46
  // override in subclass with custom Xstate options
48
- return DefaultOptionsFunc(this)
47
+ return {}
49
48
  }
50
49
 
51
50
  initStateMachine(previousState) {
@@ -128,48 +127,9 @@ class AnearEvent extends JsonApiResource {
128
127
  return await this.messaging.getSpectatorCount(this)
129
128
  }
130
129
 
131
- async createdEventCallback(participantCreator) {
132
- // You may implement createdEventCallback() in your AnearEvent sub-class
133
- }
134
-
135
- async participantEnterEventCallback(participant) {
136
- throw new Error('You must implement an async participantEnterEventCallback() in your AnearEvent sub-class');
137
- }
138
-
139
- async participantRefreshEventCallback(participant, remainingTimeout = null) {
140
- // You may implement an async participantRefreshEventCallback() in your AnearEvent sub-class
141
- }
142
-
143
- async spectatorRefreshEventCallback() {
144
- // You may implement an async spectatorRefreshEventCallback() in your AnearEvent sub-class
145
- }
146
-
147
- async participantExitEventCallback(participant) {
148
- throw new Error('You must implement an async participantExitEventCallback() in your AnearEvent sub-class');
149
- }
150
-
151
- async participantActionEventCallback(participant, actionEventName, message) {
152
- throw new Error('You must implement an async participantActionEventCallback() in your AnearEvent sub-class');
153
- }
154
-
155
- async participantTimedOutEventCallback(participant) {
156
- // You may implement participantTimedOutEventCallback() in your AnearEvent sub-class'
157
- }
158
-
159
- async eventBroadcastEventCallback(message) {
160
- // You may implement eventBroadcastEventCallback() in your AnearEvent sub-class'
161
- }
162
-
163
130
  isParticipantEventCreator(RParticipant) {
164
131
  return participant.userId === this.userId
165
132
  }
166
- async refreshParticipant(participant) {
167
- await this.participantRefreshEventCallback(participant)
168
- }
169
-
170
- async refreshSpectator() {
171
- await this.spectatorRefreshEventCallback()
172
- }
173
133
 
174
134
  async publishEventParticipantsMessage(message, timeoutMsecs=0) {
175
135
  await this.messaging.publishEventParticipantsMessage(
@@ -254,6 +214,10 @@ class AnearEvent extends JsonApiResource {
254
214
  await this.participantPurge(participant)
255
215
  }
256
216
 
217
+ spectatorView(userId) {
218
+ this.anearStateMachine.sendSpectatorViewEvent({userId})
219
+ }
220
+
257
221
  async participantPurge(participant) {
258
222
  this.participants.purge(participant)
259
223
  await participant.remove()
@@ -267,10 +231,6 @@ class AnearEvent extends JsonApiResource {
267
231
  this.anearStateMachine.sendTimeoutEvent({ participant })
268
232
  }
269
233
 
270
- async eventBroadcast(message) {
271
- await this.eventBroadcastEventCallback(message)
272
- }
273
-
274
234
  async transitionEvent(eventName='next') {
275
235
  //
276
236
  // Allows the app/game to transition the remote AnearEvent which can un/hide and event
@@ -3,7 +3,8 @@ const { createMachine, interpret, State } = require('xstate')
3
3
 
4
4
  const logger = require('../utils/Logger')
5
5
 
6
- const JoinEvent = 'JOIN'
6
+ const JoinEvent = 'PARTICIPANT_JOIN'
7
+ const SpectatorViewEvent = 'SPECTATOR_VIEW'
7
8
  const ParticipantExitEvent = 'PARTICIPANT_EXIT'
8
9
  const RefreshEvent = 'REFRESH'
9
10
  const CloseEvent = 'EVENT_CLOSE'
@@ -60,6 +61,10 @@ class AnearXstate {
60
61
  this.send(ParticipantExitEvent, params)
61
62
  }
62
63
 
64
+ sendSpectatorViewEvent(params) {
65
+ this.send(SpectatorViewEvent, params)
66
+ }
67
+
63
68
  sendTimeoutEvent(params) {
64
69
  this.send(TimeoutEvent, params)
65
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anear-js-api",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Javascript Developer API for Anear Apps",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -5,6 +5,7 @@ const AnearParticipant = require('../lib/models/AnearParticipant')
5
5
  const MockMessaging = require('../lib/messaging/__mocks__/AnearMessaging')
6
6
 
7
7
  const mockParticipantEnterHandler = jest.fn()
8
+ const mockSpectatorViewHandler = jest.fn()
8
9
  const mockParticipantRefreshHandler = jest.fn()
9
10
  const mockParticipantExitHandler = jest.fn()
10
11
  const mockParticipantActionHandler = jest.fn()
@@ -15,15 +16,19 @@ const TicTacToeMachineConfig = anearEvent => ({
15
16
  states: {
16
17
  waitingForHost: {
17
18
  on: {
18
- JOIN: {
19
+ PARTICIPANT_JOIN: {
19
20
  actions: 'enterHandler',
20
21
  target: 'waitingForOpponent'
22
+ },
23
+ SPECTATOR_VIEW: {
24
+ actions: 'viewHandler',
25
+ target: 'waitingForHost'
21
26
  }
22
27
  }
23
28
  },
24
29
  waitingForOpponent: {
25
30
  on: {
26
- JOIN: {
31
+ PARTICIPANT_JOIN: {
27
32
  actions: 'enterHandler',
28
33
  target: 'gameStart'
29
34
  },
@@ -37,6 +42,9 @@ const TicTacToeMachineConfig = anearEvent => ({
37
42
  },
38
43
  gameStart: {
39
44
  on: {
45
+ TEST_ACTION: {
46
+ actions: 'testActionHandler'
47
+ },
40
48
  BULLSEYE: {
41
49
  actions: 'actionHandler'
42
50
  },
@@ -59,10 +67,20 @@ const TicTacToeMachineOptions = anearEvent => ({
59
67
  refreshHandler: (context, event) => {
60
68
  anearEvent.myParticipantRefreshHandler(event.participant)
61
69
  },
70
+ viewHandler: (context, event) => {
71
+ anearEvent.mySpectatorViewHandler(event.userId)
72
+ },
62
73
  participantExitHandler: (context, event) => {
63
74
  anearEvent.myParticipantExitHandler(event.participant)
64
75
  },
65
76
  actionHandler: assign({score: (context, event) => context.score + event.payload.points}),
77
+ testActionHandler: (context, event) => {
78
+ anearEvent.myParticipantActionHandler(
79
+ event.participant.id,
80
+ event.type,
81
+ event.payload
82
+ )
83
+ }
66
84
  }
67
85
  })
68
86
 
@@ -81,6 +99,9 @@ class TestEvent extends AnearEvent {
81
99
  return TicTacToeMachineOptions(this)
82
100
  }
83
101
 
102
+ async mySpectatorViewHandler(...args) {
103
+ return mockSpectatorViewHandler(...args)
104
+ }
84
105
  async myParticipantEnterHandler(...args) {
85
106
  return mockParticipantEnterHandler(...args)
86
107
  }
@@ -90,37 +111,12 @@ class TestEvent extends AnearEvent {
90
111
  async myParticipantRefreshHandler(...args) {
91
112
  return mockParticipantRefreshHandler(...args)
92
113
  }
93
- }
94
-
95
-
96
- class TestEventWithDefaultXState extends AnearEvent {
97
- initContext() {
98
- return {
99
- playerScores: [83, 22]
100
- }
101
- }
102
-
103
- participantEnterEventCallback(participant) {
104
- mockParticipantEnterHandler(participant)
105
- return Promise.resolve()
106
- }
107
-
108
- participantRefreshEventCallback(participant) {
109
- mockParticipantRefreshHandler(participant)
110
- return Promise.resolve()
111
- }
112
-
113
- participantExitEventCallback(participant) {
114
- mockParticipantExitHandler(participant)
115
- return Promise.resolve()
116
- }
117
-
118
- participantActionEventCallback(participant, actionEventName, payload) {
119
- mockParticipantActionHandler(participant, actionEventName, payload)
120
- return Promise.resolve()
114
+ async myParticipantActionHandler(...args) {
115
+ return mockParticipantActionHandler(...args)
121
116
  }
122
117
  }
123
118
 
119
+
124
120
  class TestPlayer extends AnearParticipant {
125
121
  initContext() {
126
122
  return {
@@ -148,23 +144,18 @@ const newTestEvent = (hosted = false) => {
148
144
  return t
149
145
  }
150
146
 
151
- const newTestEventWithDefaultXState = testEvent => {
152
- const t = new TestEventWithDefaultXState(testEvent, TestPlayer, MessagingStub)
153
- t.startStateMachine()
154
- return t
155
- }
156
-
157
- test('participant enter with Default Xstate Config', async () => {
158
- const t = newTestEventWithDefaultXState(chatEvent)
147
+ test('participant enter', async () => {
148
+ const t = newTestEvent()
159
149
 
160
150
  const id = t.id
161
151
  expect(t.id).toBe(chatEvent.data.id)
162
152
  expect(t.relationships.user.data.type).toBe("users")
163
- expect(t.anearStateMachine.currentState.value).toBe("eventActive")
164
- expect(t.stateMachineContext.playerScores[0]).toBe(83)
153
+ expect(t.anearStateMachine.currentState.value).toBe("waitingForHost")
154
+ expect(t.stateMachineContext.score).toBe(90)
165
155
  const p1 = new TestPlayer(chatParticipant1, t)
166
156
 
167
157
  await t.participantEnter(p1)
158
+ expect(t.anearStateMachine.currentState.value).toBe("waitingForOpponent")
168
159
  await t.persist()
169
160
  await t.remove()
170
161
 
@@ -176,10 +167,25 @@ test('participant enter with Default Xstate Config', async () => {
176
167
  await p1.remove()
177
168
  })
178
169
 
179
- test('participant close with Default Xstate Config', async () => {
180
- const t = newTestEventWithDefaultXState(chatEvent)
170
+ test('spectator viewer', async () => {
171
+ const t = newTestEvent()
172
+ const userId = 999837834
173
+
174
+ const id = t.id
175
+ expect(t.anearStateMachine.currentState.value).toBe("waitingForHost")
176
+ await t.spectatorView(userId)
177
+ expect(t.anearStateMachine.currentState.value).toBe("waitingForHost")
178
+
179
+ expect(mockSpectatorViewHandler).toHaveBeenCalledTimes(1)
180
+ expect(mockSpectatorViewHandler).toHaveBeenCalledWith(userId)
181
+ })
182
+
183
+
184
+ test('participant close', async () => {
185
+ const t = newTestEvent()
181
186
 
182
187
  const p1 = new TestPlayer(chatParticipant1, t)
188
+ await t.participantEnter(p1)
183
189
 
184
190
  await t.participantExit(p1)
185
191
  await t.update()
@@ -191,29 +197,20 @@ test('participant close with Default Xstate Config', async () => {
191
197
  await t.remove()
192
198
  })
193
199
 
194
- test('participant refresh with Default Xstate Config', async () => {
195
- const t = newTestEventWithDefaultXState(chatEvent)
200
+ test('participant action', async () => {
201
+ const t = newTestEvent()
196
202
  const p1 = new TestPlayer(chatParticipant1, t)
203
+ const p2 = new TestPlayer(chatParticipant2, t)
204
+ await t.participantEnter(p1)
205
+ await t.participantEnter(p2)
197
206
 
198
- await t.refreshParticipant(p1)
199
- await t.update()
200
-
201
- expect(mockParticipantRefreshHandler).toHaveBeenCalledWith(p1)
202
- expect(mockParticipantRefreshHandler).toHaveBeenCalledTimes(1)
203
- await p1.remove()
204
- await t.remove()
205
- })
206
-
207
- test('participant action with Default Xstate Config', async () => {
208
- const t = newTestEventWithDefaultXState(chatEvent)
209
- const p1 = new TestPlayer(chatParticipant1, t)
210
207
  const eventName = "TEST_ACTION"
211
208
  const payload = {x: 1, y: 99}
212
209
 
213
210
  await t.participantAction(p1, eventName, payload)
214
211
  await t.update()
215
212
 
216
- expect(mockParticipantActionHandler).toHaveBeenCalledWith(p1, eventName, payload)
213
+ expect(mockParticipantActionHandler).toHaveBeenCalledWith(p1.id, eventName, payload)
217
214
  expect(mockParticipantActionHandler).toHaveBeenCalledTimes(1)
218
215
  await p1.remove()
219
216
  await t.remove()
@@ -1,108 +0,0 @@
1
- "use strict"
2
-
3
- // Default configuration and options funcs provide
4
- // a simple default state machine for an anear Event. This is
5
- // so we don't mandate that app developers use xState to drive
6
- // their app's state transitions. Devs simply provide callbacks
7
- // override implementations in their AnearEvent subclass.
8
- // The xState context is simply the anearEvent.context
9
- //
10
- // If a developer wants an xState machine to drive the applications
11
- // state transitions, the developer should override stateMachineConfig()
12
- // and stateMachineOptions() in their AnearEvent subclass.
13
- //
14
- const DefaultConfigFunc = (anearEvent) => {
15
-
16
- const PromiseResolveReject = {
17
- onDone: {
18
- target: 'eventActive'
19
- },
20
- onError: {
21
- actions: 'logError',
22
- target: 'eventActive'
23
- }
24
- }
25
-
26
- return {
27
- id: "defaultXstateConfig",
28
- initial: 'eventActive',
29
- states: {
30
- eventActive: {
31
- on: {
32
- JOIN: {
33
- target: 'join'
34
- },
35
- REFRESH: {
36
- target: 'refresh'
37
- },
38
- PARTICIPANT_EXIT: {
39
- target: 'participant_exit'
40
- },
41
- TIMEOUT: {
42
- target: 'timeout'
43
- },
44
- '*': {
45
- // default wildcard state is presumed to be a custom ACTION name
46
- // embedded in a anear-action-click property in the app's HTML
47
- target: 'action'
48
- }
49
- }
50
- },
51
- join: {
52
- invoke: {
53
- id: 'join',
54
- src: 'joinEventHandler',
55
- ...PromiseResolveReject
56
- }
57
- },
58
- refresh: {
59
- invoke: {
60
- id: 'refresh',
61
- src: 'refreshEventHandler',
62
- ...PromiseResolveReject
63
- }
64
- },
65
- participant_exit: {
66
- invoke: {
67
- id: 'participant_exit',
68
- src: 'participantExitEventHandler',
69
- ...PromiseResolveReject
70
- }
71
- },
72
- timeout: {
73
- invoke: {
74
- id: 'timeout',
75
- src: 'timeoutEventHandler',
76
- ...PromiseResolveReject
77
- }
78
- },
79
- action: {
80
- invoke: {
81
- id: 'action',
82
- src: 'actionEventHandler',
83
- ...PromiseResolveReject
84
- }
85
- }
86
- }
87
- }
88
- }
89
-
90
- const DefaultOptionsFunc = anearEvent => {
91
- return {
92
- actions: {
93
- logError: (context, event) => logger.error(`error message: ${event.data}`)
94
- },
95
- services: {
96
- joinEventHandler: (context, event) => anearEvent.participantEnterEventCallback(event.participant),
97
- refreshEventHandler: (context, event) => anearEvent.participantRefreshEventCallback(event.participant, event.remainingTimeout),
98
- participantExitEventHandler: (context, event) => anearEvent.participantExitEventCallback(event.participant),
99
- timeoutEventHandler: (context, event) => anearEvent.participantTimedOutEventCallback(event.participant),
100
- actionEventHandler: (context, event) => anearEvent.participantActionEventCallback(event.participant, event.type, event.payload)
101
- }
102
- }
103
- }
104
-
105
- module.exports = {
106
- DefaultConfigFunc,
107
- DefaultOptionsFunc
108
- }