@player-tools/devtools-client 0.2.2--canary.19.409 → 0.2.2--canary.17.433

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.
@@ -1,70 +1,8 @@
1
- import {
2
- type Message,
3
- clearStore,
4
- playerFlowStartAction,
5
- playerInitAction,
6
- playerRemoveAction,
7
- playerTimelineAction,
8
- playerViewUpdateAction,
9
- selectedPlayerAction,
10
- } from '@player-tools/devtools-common';
11
- import type { Store } from 'redux';
12
- import { GET_DATA_BINDING_DETAILS } from './aliases';
13
-
14
1
  export * from './actions';
15
2
  export * from './aliases';
3
+ export * from './listeners';
4
+ export * from './middleware';
16
5
  export * from './reducers';
17
-
18
- export function handleMessage(store: Store, message: Message) {
19
- switch (message.type) {
20
- case 'runtime-init':
21
- store.dispatch(clearStore());
22
- break;
23
- case 'player-init':
24
- store.dispatch(playerInitAction(message));
25
- store.dispatch(selectedPlayerAction());
26
- break;
27
- case 'player-removed':
28
- store.dispatch(playerRemoveAction(message.playerID));
29
- store.dispatch(selectedPlayerAction());
30
- break;
31
- case 'player-flow-start':
32
- store.dispatch(playerFlowStartAction(message));
33
- store.dispatch(playerTimelineAction(message));
34
- store.dispatch({
35
- type: GET_DATA_BINDING_DETAILS,
36
- payload: { playerID: message.playerID, binding: '' },
37
- });
38
- break;
39
- case 'player-log-event':
40
- store.dispatch(playerTimelineAction(message));
41
- break;
42
- case 'player-view-update-event':
43
- store.dispatch(playerViewUpdateAction(message));
44
- break;
45
- case 'player-data-change-event': {
46
- const { players } = store.getState();
47
-
48
- if (
49
- players.activePlayers[message.playerID] &&
50
- players.activePlayers[message.playerID].dataState.selectedBinding
51
- ) {
52
- store.dispatch({
53
- type: GET_DATA_BINDING_DETAILS,
54
- payload: message,
55
- });
56
- }
57
-
58
- store.dispatch({
59
- type: GET_DATA_BINDING_DETAILS,
60
- payload: { playerID: message.playerID, binding: '' },
61
- });
62
- store.dispatch(playerTimelineAction(message));
63
- break;
64
- }
65
-
66
- default:
67
- console.warn(`Unhandled event: ${JSON.stringify(message)}`);
68
- break;
69
- }
70
- }
6
+ export * from './selectors';
7
+ export * from './state';
8
+ export * from './store';
@@ -0,0 +1,8 @@
1
+ import { Events } from "@player-tools/devtools-common";
2
+ import { Dispatch } from "redux";
3
+ import { EventActions } from "./actions";
4
+
5
+ /** Utility method to filter known events from a supplied message and dispatch the corresponding action */
6
+ export const dispatchEvents = (dispatch: Dispatch) => (message: any) => {
7
+ if (Events.isEvent(message)) dispatch(EventActions[message.type](message as any))
8
+ }
@@ -0,0 +1,64 @@
1
+ import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit";
2
+ import { Actions, EventActions } from "./actions";
3
+ import { GET_DATA_BINDING_DETAILS } from "./aliases";
4
+ import { type StoreState } from './state';
5
+
6
+ /**
7
+ * Listener middleware that will be consumed by default when creating the devtools store.
8
+ * Exported such that clients can configure additional side effects.
9
+ */
10
+ export const listenerMiddleware = createListenerMiddleware<StoreState>();
11
+
12
+ listenerMiddleware.startListening({
13
+ matcher: isAnyOf(
14
+ EventActions['player-data-change-event'],
15
+ EventActions['player-log-event'],
16
+ EventActions['player-flow-start'],
17
+ ),
18
+ effect: (action, api) => {
19
+ api.dispatch(Actions['player-timeline-event'](action.payload));
20
+ },
21
+ });
22
+
23
+ listenerMiddleware.startListening({
24
+ actionCreator: EventActions['runtime-init'],
25
+ effect: (_, api) => {
26
+ api.dispatch(Actions["clear-store"]())
27
+ }
28
+ })
29
+
30
+ listenerMiddleware.startListening({
31
+ matcher: isAnyOf(
32
+ EventActions["player-init"],
33
+ EventActions["player-removed"],
34
+ ),
35
+ effect: (_, api) => {
36
+ api.dispatch(Actions["selected-player"]())
37
+ }
38
+ });
39
+
40
+ listenerMiddleware.startListening({
41
+ matcher: isAnyOf(
42
+ EventActions["player-flow-start"],
43
+ EventActions["player-data-change-event"],
44
+ ),
45
+ effect: (action, api) => {
46
+ const { players } = api.getState();
47
+ const { playerID } = action.payload;
48
+
49
+ if (
50
+ players.activePlayers[playerID] &&
51
+ players.activePlayers[playerID].dataState.selectedBinding
52
+ ) {
53
+ api.dispatch({
54
+ type: GET_DATA_BINDING_DETAILS,
55
+ payload: { playerID, binding: players.activePlayers[playerID].dataState.selectedBinding },
56
+ });
57
+ }
58
+
59
+ api.dispatch({
60
+ type: GET_DATA_BINDING_DETAILS,
61
+ payload: { playerID, binding: '' },
62
+ })
63
+ }
64
+ })
@@ -1,118 +1,247 @@
1
- import type { ActionReducerMapBuilder } from '@reduxjs/toolkit';
2
- import type { PlayersState } from '@player-tools/devtools-common';
3
- import type { AsyncRPCActions } from './actions';
4
-
5
- /**
6
- * Callback function that adds cases for async actions for the player.
7
- * @param playerReducerCallback
8
- * @returns
9
- */
10
- export const buildPlayerReducerCallback =
11
- (actions: AsyncRPCActions) =>
12
- (builder: ActionReducerMapBuilder<PlayersState>) => {
13
- builder.addCase(
14
- actions['player-runtime-info-request'].fulfilled,
15
- (state, action) => {
16
- const { activePlayers, selectedPlayerId } = state;
17
-
18
- if (!selectedPlayerId) {
19
- return;
20
- }
21
-
22
- const data =
23
- action.payload && Object.keys(action.payload).length > 0
24
- ? action.payload
25
- : null;
26
- activePlayers[selectedPlayerId].flowInfo = data;
1
+ import { type ActionReducerMapBuilder, createReducer } from '@reduxjs/toolkit';
2
+ import type { PlayersState } from './state';
3
+ import { Actions, EventActions, type MethodThunks } from './actions';
4
+
5
+ const initialState = {
6
+ selectedPlayerId: null,
7
+ activePlayers: {},
8
+ } as PlayersState;
9
+
10
+ const methodsReducer = (methods: MethodThunks) => (builder: ActionReducerMapBuilder<PlayersState>) => {
11
+ builder.addCase(
12
+ methods['player-runtime-info-request'].fulfilled,
13
+ (state, action) => {
14
+ const { activePlayers, selectedPlayerId } = state;
15
+
16
+ if (!selectedPlayerId) {
17
+ return;
27
18
  }
28
- );
29
19
 
30
- builder.addCase(
31
- actions['player-config-request'].fulfilled,
32
- (state, action) => {
33
- const { activePlayers, selectedPlayerId } = state;
34
-
35
- if (!selectedPlayerId) {
36
- return;
37
- }
38
-
39
- activePlayers[selectedPlayerId].configState = action.payload;
20
+ const data =
21
+ action.payload && Object.keys(action.payload).length > 0
22
+ ? action.payload
23
+ : null;
24
+ activePlayers[selectedPlayerId].flowInfo = data;
25
+ }
26
+ );
27
+
28
+ builder.addCase(
29
+ methods['player-config-request'].fulfilled,
30
+ (state, action) => {
31
+ const { activePlayers, selectedPlayerId } = state;
32
+
33
+ if (!selectedPlayerId) {
34
+ return;
40
35
  }
41
- );
42
36
 
43
- builder.addCase(
44
- actions['player-view-details-request'].fulfilled,
45
- (state, action) => {
46
- const { activePlayers, selectedPlayerId } = state;
37
+ activePlayers[selectedPlayerId].configState = action.payload;
38
+ }
39
+ );
47
40
 
48
- if (!selectedPlayerId) {
49
- return;
50
- }
41
+ builder.addCase(
42
+ methods['player-view-details-request'].fulfilled,
43
+ (state, action) => {
44
+ const { activePlayers, selectedPlayerId } = state;
51
45
 
52
- activePlayers[selectedPlayerId].view = action.payload?.lastViewUpdate;
53
- }
54
- );
55
-
56
- builder.addCase(
57
- actions['player-data-binding-details'].fulfilled,
58
- (state, action) => {
59
- const {
60
- meta: {
61
- arg: { binding, playerID },
62
- },
63
- payload,
64
- } = action;
65
- const { activePlayers } = state;
66
-
67
- if (!playerID || !activePlayers[playerID]) {
68
- return;
69
- }
70
-
71
- if (binding === '') {
72
- activePlayers[playerID].dataState.allBindings = payload;
73
- return;
74
- }
75
-
76
- activePlayers[playerID].dataState.selectedBinding = payload;
77
- }
78
- );
79
-
80
- builder.addCase(
81
- actions['player-execute-expression'].fulfilled,
82
- (state, action) => {
83
- const { activePlayers, selectedPlayerId } = state;
84
-
85
- if (!selectedPlayerId) {
86
- return;
87
- }
88
-
89
- activePlayers[selectedPlayerId].consoleState?.history?.push({
90
- id: action.meta.requestId,
91
- result: action.payload,
92
- expression: action.payload?.exp ?? '',
93
- });
46
+ if (!selectedPlayerId) {
47
+ return;
94
48
  }
95
- );
96
49
 
97
- builder.addCase(
98
- actions['player-start-profiler-request'].fulfilled,
99
- (state, action) => {
100
- const { activePlayers, selectedPlayerId } = state;
101
-
102
- if (!selectedPlayerId) return;
50
+ activePlayers[selectedPlayerId].view = action.payload?.lastViewUpdate;
51
+ }
52
+ );
53
+
54
+ builder.addCase(
55
+ methods['player-data-binding-details'].fulfilled,
56
+ (state, action) => {
57
+ const {
58
+ meta: {
59
+ arg: { params: { binding, playerID } },
60
+ },
61
+ payload,
62
+ } = action;
63
+ const { activePlayers } = state;
64
+
65
+ if (!playerID || !activePlayers[playerID]) {
66
+ return;
67
+ }
103
68
 
104
- activePlayers[selectedPlayerId].profilerInfo = action.payload?.data;
69
+ if (binding === '') {
70
+ activePlayers[playerID].dataState.allBindings = payload;
71
+ return;
105
72
  }
106
- );
107
73
 
108
- builder.addCase(
109
- actions['player-stop-profiler-request'].fulfilled,
110
- (state, action) => {
111
- const { activePlayers, selectedPlayerId } = state;
74
+ activePlayers[playerID].dataState.selectedBinding = payload;
75
+ }
76
+ );
112
77
 
113
- if (!selectedPlayerId) return;
78
+ builder.addCase(
79
+ methods['player-execute-expression'].fulfilled,
80
+ (state, action) => {
81
+ const { activePlayers, selectedPlayerId } = state;
114
82
 
115
- activePlayers[selectedPlayerId].profilerInfo = action.payload?.data;
83
+ if (!selectedPlayerId) {
84
+ return;
116
85
  }
117
- );
118
- };
86
+
87
+ activePlayers[selectedPlayerId].consoleState?.history?.push({
88
+ id: action.meta.requestId,
89
+ result: action.payload,
90
+ expression: action.payload?.exp ?? '',
91
+ });
92
+ }
93
+ );
94
+
95
+ builder.addCase(
96
+ methods['player-start-profiler-request'].fulfilled,
97
+ (state, action) => {
98
+ const { activePlayers, selectedPlayerId } = state;
99
+
100
+ if (!selectedPlayerId) return;
101
+
102
+ activePlayers[selectedPlayerId].profilerInfo = action.payload?.data;
103
+ }
104
+ );
105
+
106
+ builder.addCase(
107
+ methods['player-stop-profiler-request'].fulfilled,
108
+ (state, action) => {
109
+ const { activePlayers, selectedPlayerId } = state;
110
+
111
+ if (!selectedPlayerId) return;
112
+
113
+ activePlayers[selectedPlayerId].profilerInfo = action.payload?.data;
114
+ }
115
+ );
116
+ };
117
+
118
+ const eventsReducer = (builder: ActionReducerMapBuilder<PlayersState>) => {
119
+ builder.addCase(EventActions['player-init'], (state, action) => {
120
+ const {
121
+ payload: { version, playerID },
122
+ } = action;
123
+ state.activePlayers[playerID] = {
124
+ timelineEvents: [],
125
+ dataState: {},
126
+ consoleState: { history: [] },
127
+ };
128
+ state.version = version;
129
+ });
130
+
131
+ builder.addCase(EventActions['player-removed'], (state, action) => {
132
+ delete state.activePlayers[action.payload.playerID];
133
+ });
134
+
135
+ builder.addCase(EventActions['player-flow-start'], (state, action) => {
136
+ const {
137
+ payload: { flow, playerID },
138
+ } = action;
139
+
140
+ if (!state.activePlayers[playerID]) {
141
+ state.activePlayers[playerID] = {
142
+ flowInfo: { currentFlow: flow },
143
+ timelineEvents: [],
144
+ dataState: {},
145
+ consoleState: { history: [] },
146
+ };
147
+ state.selectedPlayerId = playerID;
148
+ return;
149
+ }
150
+
151
+ state.activePlayers[playerID].flowInfo = {
152
+ ...state.activePlayers[playerID].flowInfo,
153
+ currentFlow: flow,
154
+ };
155
+ });
156
+
157
+ builder.addCase(EventActions['player-view-update-event'], (state, action) => {
158
+ const {
159
+ payload: { playerID, update },
160
+ } = action;
161
+
162
+ if (!state.activePlayers[playerID]) {
163
+ state.activePlayers[playerID] = {
164
+ view: update,
165
+ timelineEvents: [],
166
+ dataState: {},
167
+ consoleState: { history: [] },
168
+ };
169
+ state.selectedPlayerId = playerID;
170
+ return;
171
+ }
172
+
173
+ state.activePlayers[playerID].view = update;
174
+ });
175
+ };
176
+
177
+ const actionsReducer = (builder: ActionReducerMapBuilder<PlayersState>) => {
178
+ builder.addCase(Actions['selected-player'], (state, action) => {
179
+ if (action.payload) {
180
+ state.selectedPlayerId = action.payload;
181
+ return;
182
+ }
183
+
184
+ state.selectedPlayerId = Object.keys(state.activePlayers)[0] || null;
185
+ });
186
+
187
+ builder.addCase(Actions['player-timeline-event'], (state, action) => {
188
+ const {
189
+ payload: { playerID },
190
+ } = action;
191
+
192
+ if (!state.activePlayers[playerID]) {
193
+ state.activePlayers[playerID] = {
194
+ timelineEvents: [action.payload],
195
+ dataState: {},
196
+ consoleState: { history: [] },
197
+ };
198
+ state.selectedPlayerId = playerID;
199
+ return;
200
+ }
201
+
202
+ state.activePlayers[playerID].timelineEvents.push(action.payload);
203
+ });
204
+
205
+ builder.addCase(Actions['clear-selected-data-details'], (state) => {
206
+ const { activePlayers, selectedPlayerId } = state;
207
+
208
+ if (!selectedPlayerId || !activePlayers[selectedPlayerId]) {
209
+ return;
210
+ }
211
+
212
+ activePlayers[selectedPlayerId].dataState.selectedBinding = undefined;
213
+ });
214
+
215
+ builder.addCase(Actions['clear-console'], (state) => {
216
+ const { activePlayers, selectedPlayerId } = state;
217
+
218
+ if (!selectedPlayerId) {
219
+ return;
220
+ }
221
+
222
+ activePlayers[selectedPlayerId].consoleState = {
223
+ history: [],
224
+ };
225
+ });
226
+
227
+ builder.addCase(Actions['clear-logs'], (state) => {
228
+ const { activePlayers, selectedPlayerId } = state;
229
+
230
+ if (!selectedPlayerId) {
231
+ return;
232
+ }
233
+
234
+ activePlayers[selectedPlayerId].timelineEvents = [];
235
+ });
236
+
237
+ builder.addCase(Actions['clear-store'], () => {
238
+ return initialState;
239
+ });
240
+ };
241
+
242
+ export const playersReducer = (methods: MethodThunks) =>
243
+ createReducer<PlayersState>(initialState, (builder) => {
244
+ actionsReducer(builder)
245
+ eventsReducer(builder)
246
+ methodsReducer(methods)(builder)
247
+ });
@@ -0,0 +1,133 @@
1
+ import { createSelector } from '@reduxjs/toolkit';
2
+ import type { StoreState, PlayersState } from './state';
3
+
4
+ /**
5
+ * Selects the player state
6
+ * @param state
7
+ * @returns
8
+ */
9
+ const selectPlayers = (state: StoreState) => {
10
+ return state.players;
11
+ };
12
+
13
+ /**
14
+ * Selects all the active players.
15
+ */
16
+ const selectActivePlayers = createSelector(
17
+ selectPlayers,
18
+ (players: PlayersState) => players.activePlayers
19
+ );
20
+
21
+ export const selectPlayerVersion = createSelector(
22
+ selectPlayers,
23
+ (players: PlayersState) => players.version
24
+ );
25
+
26
+ export const selectPlayerIds = createSelector(
27
+ selectActivePlayers,
28
+ (activePlayers) => Object.keys(activePlayers) || []
29
+ );
30
+
31
+ /**
32
+ * Selects the selected/currently active player Id.
33
+ */
34
+ export const selectSelectedPlayerId = createSelector(
35
+ selectPlayers,
36
+ (players: PlayersState) => players.selectedPlayerId
37
+ );
38
+
39
+ export const selectCurrentPlayer = createSelector(
40
+ selectActivePlayers,
41
+ selectSelectedPlayerId,
42
+ (activePlayers, selectedPlayerId) => {
43
+ if (!selectedPlayerId) {
44
+ return null;
45
+ }
46
+
47
+ return activePlayers[selectedPlayerId];
48
+ }
49
+ );
50
+
51
+ export const selectConfig = createSelector(
52
+ selectCurrentPlayer,
53
+ (currentPlayer) => {
54
+ return currentPlayer?.configState ?? null;
55
+ }
56
+ );
57
+
58
+ const selectData = createSelector(selectCurrentPlayer, (currentPlayer) => {
59
+ return currentPlayer?.dataState;
60
+ });
61
+
62
+ export const selectFlowInfo = createSelector(
63
+ selectCurrentPlayer,
64
+ (currentPlayer) => {
65
+ if (!currentPlayer) {
66
+ return null;
67
+ }
68
+
69
+ return currentPlayer?.flowInfo;
70
+ }
71
+ );
72
+
73
+ export const selectCurrentFlow = createSelector(
74
+ selectFlowInfo,
75
+ (flowInfo) => {
76
+ return flowInfo?.currentFlow;
77
+ }
78
+ );
79
+
80
+ export const selectCurrentTopic = createSelector(
81
+ selectCurrentFlow,
82
+ (currentFlow) => {
83
+ return currentFlow?.topic;
84
+ }
85
+ );
86
+
87
+ export const selectEvents = createSelector(
88
+ selectCurrentPlayer,
89
+ (currentPlayer) => {
90
+ if (!currentPlayer) {
91
+ return [];
92
+ }
93
+
94
+ return currentPlayer?.timelineEvents;
95
+ }
96
+ );
97
+
98
+ export const selectView = createSelector(
99
+ selectCurrentPlayer,
100
+ (currentPlayer) => {
101
+ if (!currentPlayer) {
102
+ return null;
103
+ }
104
+
105
+ return currentPlayer?.view;
106
+ }
107
+ );
108
+
109
+ export const selectAllBindings = createSelector(selectData, (data) => {
110
+ return data?.allBindings;
111
+ });
112
+
113
+ export const selectSelectedBinding = createSelector(selectData, (data) => {
114
+ return data?.selectedBinding;
115
+ });
116
+
117
+ export const selectConsole = createSelector(
118
+ selectCurrentPlayer,
119
+ (currentPlayer) => {
120
+ if (!currentPlayer) {
121
+ return { history: [] };
122
+ }
123
+
124
+ return currentPlayer.consoleState;
125
+ }
126
+ );
127
+
128
+ export const selectProfiler = createSelector(
129
+ selectCurrentPlayer,
130
+ (currentPlayer) => {
131
+ return currentPlayer?.profilerInfo;
132
+ }
133
+ );