goblin-desktop 4.2.0 → 4.2.2

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.
Files changed (144) hide show
  1. package/.editorconfig +9 -9
  2. package/.zou-flow +2 -2
  3. package/LICENSE +21 -21
  4. package/README.md +108 -108
  5. package/builders/builders.js +3 -3
  6. package/builders/wizard.js +462 -462
  7. package/contexts.js +13 -13
  8. package/desktop-manager.js +10 -10
  9. package/desktop-window.js +13 -13
  10. package/desktop.js +13 -13
  11. package/eslint.config.js +65 -65
  12. package/lib/service.js +137 -137
  13. package/package.json +48 -48
  14. package/password-wizard.js +24 -24
  15. package/quest-run-wizard.js +24 -24
  16. package/taskbar.js +13 -13
  17. package/widgets/audio/sfx.js +177 -177
  18. package/widgets/board/view.js +37 -37
  19. package/widgets/board/widget.js +65 -65
  20. package/widgets/contexts/logic-handlers.js +36 -36
  21. package/widgets/contexts/service.js +40 -40
  22. package/widgets/contexts/view.js +21 -21
  23. package/widgets/contexts/widget.js +147 -147
  24. package/widgets/datagrid/datagrid-entity.js +82 -82
  25. package/widgets/datagrid/datagrid-headers.js +274 -274
  26. package/widgets/datagrid/styles.js +13 -13
  27. package/widgets/datagrid/widget.js +192 -192
  28. package/widgets/datagrid-cell/styles.js +52 -52
  29. package/widgets/datagrid-cell/widget.js +41 -41
  30. package/widgets/datagrid-item/styles.js +15 -15
  31. package/widgets/datagrid-item/widget.js +74 -74
  32. package/widgets/default/view.js +160 -160
  33. package/widgets/desktop/compensator.js +9 -9
  34. package/widgets/desktop/logic-handlers.js +243 -243
  35. package/widgets/desktop/reducer.js +35 -35
  36. package/widgets/desktop/service.js +858 -858
  37. package/widgets/desktop/styles.js +28 -28
  38. package/widgets/desktop/widget.js +191 -191
  39. package/widgets/desktop-clock/styles.js +69 -69
  40. package/widgets/desktop-clock/widget.js +387 -387
  41. package/widgets/desktop-clock-clock/styles.js +56 -56
  42. package/widgets/desktop-clock-clock/widget.js +96 -96
  43. package/widgets/desktop-clock-menu/styles.js +129 -129
  44. package/widgets/desktop-clock-menu/widget.js +63 -63
  45. package/widgets/desktop-connection-status/reducer.js +15 -15
  46. package/widgets/desktop-connection-status/styles.js +44 -44
  47. package/widgets/desktop-connection-status/widget.js +134 -134
  48. package/widgets/desktop-content/widget.js +68 -68
  49. package/widgets/desktop-footer/reducer.js +31 -31
  50. package/widgets/desktop-footer/styles.js +36 -36
  51. package/widgets/desktop-footer/widget.js +52 -52
  52. package/widgets/desktop-monitors/styles.js +155 -155
  53. package/widgets/desktop-monitors/widget.js +272 -272
  54. package/widgets/desktop-notebook/styles.js +155 -155
  55. package/widgets/desktop-notebook/widget.js +252 -252
  56. package/widgets/desktop-notifications/styles.js +147 -147
  57. package/widgets/desktop-notifications/widget.js +231 -231
  58. package/widgets/desktop-scale/reducer.js +15 -15
  59. package/widgets/desktop-scale/styles.js +49 -49
  60. package/widgets/desktop-scale/widget.js +173 -172
  61. package/widgets/desktop-state-monitor/styles.js +72 -72
  62. package/widgets/desktop-state-monitor/widget.js +123 -123
  63. package/widgets/desktop-taskbar/widget.js +57 -57
  64. package/widgets/desktop-themes-menu/widget.js +121 -121
  65. package/widgets/desktop-topbar/widget.js +201 -201
  66. package/widgets/desktop-window/service.js +56 -56
  67. package/widgets/desktop-window/styles.js +22 -22
  68. package/widgets/desktop-window/widget.js +70 -70
  69. package/widgets/detail/compensator.js +17 -17
  70. package/widgets/detail/view.js +40 -40
  71. package/widgets/detail/widget.js +119 -119
  72. package/widgets/editor/widget.js +82 -82
  73. package/widgets/entity-alerts/styles.js +77 -77
  74. package/widgets/entity-alerts/widget.js +328 -328
  75. package/widgets/entity-list/styles.js +66 -66
  76. package/widgets/entity-list/view.js +36 -36
  77. package/widgets/entity-list/widget.js +209 -209
  78. package/widgets/entity-list-item/widget.js +68 -68
  79. package/widgets/entity-row/styles.js +105 -105
  80. package/widgets/entity-row/widget.js +524 -524
  81. package/widgets/entity-row-button/styles.js +46 -46
  82. package/widgets/entity-row-button/widget.js +57 -57
  83. package/widgets/entity-view/reducer.js +20 -20
  84. package/widgets/entity-view/styles.js +90 -90
  85. package/widgets/entity-view/widget.js +601 -601
  86. package/widgets/facet-checkbox/styles.js +17 -17
  87. package/widgets/facet-checkbox/widget.js +43 -43
  88. package/widgets/facet-filter/widget.js +94 -94
  89. package/widgets/facet-filter-add/styles.js +30 -30
  90. package/widgets/facet-filter-add/widget.js +105 -105
  91. package/widgets/facet-filter-button/styles.js +74 -74
  92. package/widgets/facet-filter-button/widget.js +214 -214
  93. package/widgets/facet-filter-list-dialog/styles.js +59 -59
  94. package/widgets/facet-filter-list-dialog/widget.js +273 -273
  95. package/widgets/facet-filter-list-dialog-footer/styles.js +22 -22
  96. package/widgets/facet-filter-list-dialog-footer/widget.js +105 -105
  97. package/widgets/facet-filter-range-dialog/styles.js +82 -82
  98. package/widgets/facet-filter-range-dialog/widget.js +399 -399
  99. package/widgets/facet-filter-range-dialog-footer/styles.js +22 -22
  100. package/widgets/facet-filter-range-dialog-footer/widget.js +182 -182
  101. package/widgets/gamepad/widget.js +75 -75
  102. package/widgets/helpers/facet-helpers.js +105 -105
  103. package/widgets/hinter/reducer.js +35 -35
  104. package/widgets/hinter/styles.js +79 -79
  105. package/widgets/hinter/view.js +32 -32
  106. package/widgets/hinter/widget.js +289 -291
  107. package/widgets/junction/styles.js +22 -22
  108. package/widgets/junction/widget.js +50 -50
  109. package/widgets/main-tab-menu/styles.js +17 -17
  110. package/widgets/main-tab-menu/widget.js +130 -130
  111. package/widgets/map/view.js +49 -49
  112. package/widgets/map/widget.js +64 -64
  113. package/widgets/monitor/reducer.js +15 -15
  114. package/widgets/monitor/widget.js +60 -60
  115. package/widgets/navigating-layer/widget.js +25 -25
  116. package/widgets/notifications-button/widget.js +44 -44
  117. package/widgets/password-wizard/service.js +53 -53
  118. package/widgets/password-wizard/ui.js +66 -66
  119. package/widgets/plugin/reducer.js +19 -19
  120. package/widgets/plugin/styles.js +294 -294
  121. package/widgets/plugin/widget.js +622 -622
  122. package/widgets/quest-run-wizard/service.js +49 -49
  123. package/widgets/quest-run-wizard/ui.js +25 -25
  124. package/widgets/search/styles.js +80 -80
  125. package/widgets/search/widget.js +283 -285
  126. package/widgets/simple/view.js +28 -28
  127. package/widgets/status-filters/widget.js +121 -121
  128. package/widgets/tab/styles.js +16 -16
  129. package/widgets/tab/widget.js +89 -89
  130. package/widgets/tab-content/widget.js +35 -35
  131. package/widgets/tabs/widget.js +48 -48
  132. package/widgets/taskbar/service.js +99 -99
  133. package/widgets/taskbar/view.js +27 -27
  134. package/widgets/taskbar/widget.js +167 -167
  135. package/widgets/widget-doc-caller/reducer.js +15 -15
  136. package/widgets/widget-doc-caller/styles.js +20 -20
  137. package/widgets/widget-doc-caller/widget.js +55 -55
  138. package/widgets/wizard/widget.js +298 -298
  139. package/widgets/wizard-buttons/widget.js +111 -111
  140. package/widgets/workitem/styles.js +286 -286
  141. package/widgets/workitem/view.js +62 -62
  142. package/widgets/workitem/widget.js +988 -988
  143. package/widgets/workitem-dialog/widget.js +86 -86
  144. package/widgets/workitem-fields/widget.js +64 -64
@@ -1,858 +1,858 @@
1
- 'use strict';
2
-
3
- const path = require('path');
4
- const Goblin = require('xcraft-core-goblin');
5
- const watt = require('gigawatts');
6
- const goblinName = path.basename(module.parent.filename, '.js');
7
- const StringBuilder = require('goblin-nabu/lib/string-builder.js');
8
- const locks = require('xcraft-core-utils/lib/locks.js');
9
- const desktopLock = locks.getMutex;
10
- // Define initial logic values
11
- const logicState = {};
12
-
13
- // Define logic handlers according rc.json
14
- const logicHandlers = require('./logic-handlers.js');
15
- /******************************************************************************/
16
-
17
- // Register quest's according rc.json
18
- Goblin.registerQuest(goblinName, 'create', function* (
19
- quest,
20
- clientSessionId,
21
- labId,
22
- username,
23
- session,
24
- configuration,
25
- routes,
26
- mainGoblin // FIXME: replace mainGoblin by mainGoblinModule
27
- ) {
28
- if (clientSessionId) {
29
- quest.goblin.setX('clientSessionId', clientSessionId);
30
- } else {
31
- quest.log.warn('no clientSessionId provided to the new desktop');
32
- }
33
- quest.goblin.setX('labId', labId);
34
- quest.goblin.setX('configuration', configuration);
35
- // CREATE DEFAULT CONTEXT MANAGER
36
- yield quest.create('contexts', {
37
- id: `contexts@${quest.goblin.id}`,
38
- desktopId: quest.goblin.id,
39
- });
40
-
41
- //CREATE TASKBAR (TASK LAUNCHER)
42
- quest.create('taskbar', {
43
- id: `taskbar@${quest.goblin.id}`,
44
- desktopId: quest.goblin.id,
45
- });
46
-
47
- if (!mainGoblin && configuration) {
48
- mainGoblin = configuration.mainGoblin;
49
- }
50
-
51
- if (mainGoblin) {
52
- let useNabu = mainGoblin === 'nabu';
53
-
54
- if (!useNabu) {
55
- const mainConfig = require('xcraft-core-etc')().load(
56
- `goblin-${mainGoblin}`
57
- );
58
- useNabu = mainConfig?.profile?.useNabu === true;
59
- }
60
-
61
- // CREATE NABU TOOLBAR IF NEEDED
62
- if (useNabu) {
63
- const toolbarId = `nabu-toolbar@${quest.goblin.id}`;
64
- yield quest.create('nabu-toolbar', {
65
- id: toolbarId,
66
- desktopId: quest.goblin.id,
67
- enabled: false,
68
- show: true,
69
- });
70
- }
71
- }
72
-
73
- quest.do({
74
- id: quest.goblin.id,
75
- username,
76
- session,
77
- profileKey: configuration && configuration.id,
78
- });
79
-
80
- quest.log.info(() => `Desktop ${quest.goblin.id} created!`);
81
- const id = quest.goblin.id;
82
-
83
- quest.goblin.defer(
84
- quest.sub(
85
- `*::*.${quest.goblin.id.split('@')[1]}.desktop-notification-broadcasted`,
86
- function* (err, {msg, resp}) {
87
- yield resp.cmd(`${goblinName}.add-notification`, {id, ...msg.data});
88
- }
89
- )
90
- );
91
-
92
- quest.goblin.defer(
93
- quest.sub(`*::*.${quest.goblin.id}.<add-workitem-requested>`, function* (
94
- err,
95
- {msg, resp}
96
- ) {
97
- yield resp.cmd(`${goblinName}.add-workitem`, {id, ...msg.data});
98
- })
99
- );
100
-
101
- quest.goblin.defer(
102
- quest.sub(`*::*.${quest.goblin.id}.<remove-workitem-requested>`, function* (
103
- err,
104
- {msg, resp}
105
- ) {
106
- yield resp.cmd(`${goblinName}.removeWorkitem`, {id, ...msg.data});
107
- })
108
- );
109
-
110
- return quest.goblin.id;
111
- });
112
-
113
- /******************************************************************************/
114
-
115
- Goblin.registerQuest(goblinName, 'change-locale', function (quest, locale) {
116
- const labId = quest.goblin.getX('labId');
117
- quest.evt(`<${labId}>.user-locale-changed`, {locale});
118
- });
119
-
120
- Goblin.registerQuest(goblinName, 'setDefaultNewWorkitem', function (
121
- quest,
122
- workitem
123
- ) {
124
- quest.goblin.setX('newWorkitemDef', workitem);
125
- });
126
-
127
- Goblin.registerQuest(goblinName, 'openNewWorkitem', function* (
128
- quest,
129
- clientSessionId
130
- ) {
131
- const workitem = quest.goblin.getX('newWorkitemDef');
132
- if (!workitem) {
133
- return;
134
- }
135
- yield quest.me.addWorkitem({
136
- clientSessionId,
137
- workitem: {...workitem}, //!newref
138
- navigate: true,
139
- });
140
- });
141
- /******************************************************************************/
142
-
143
- Goblin.registerQuest(goblinName, 'removeWorkitem', function* (
144
- quest,
145
- workitemId,
146
- close,
147
- navToLastWorkitem
148
- ) {
149
- const state = quest.goblin.getState();
150
- const workitem = state.get(`workitems.${workitemId}`);
151
- if (!workitem) {
152
- quest.log.dbg(`Skipping ${workitemId} remove...`);
153
- return;
154
- }
155
-
156
- yield desktopLock.lock(workitemId);
157
- quest.defer(() => desktopLock.unlock(workitemId));
158
-
159
- yield quest.doSync({workitemId});
160
-
161
- const api = quest.getAPI(workitemId);
162
- if (api && close) {
163
- if (api.close) {
164
- yield api.close({kind: 'kill'});
165
- }
166
- }
167
-
168
- yield quest.kill([workitemId]);
169
-
170
- const kind = workitem.get('kind');
171
- if (navToLastWorkitem && kind !== 'dialog') {
172
- yield quest.me.navToLastWorkitem();
173
- }
174
-
175
- if (kind === 'dialog') {
176
- const contextId = workitem.get('context');
177
- quest.dispatch('setCurrentDialogByContext', {
178
- contextId,
179
- workitemId: null,
180
- });
181
- }
182
- quest.log.dbg(`Removing ${workitemId}...[DONE]`);
183
- });
184
-
185
- /******************************************************************************/
186
- const doAdd = watt(function* (
187
- quest,
188
- widgetId,
189
- clientSessionId,
190
- workitem,
191
- navigate
192
- ) {
193
- const desk = quest.me;
194
- const desktopId = desk.id;
195
-
196
- /* Manage `maxInstances` property which is useful to limit the quantity
197
- * of instances. If the `navigate` property is passed to true, then
198
- * a navigate is performed with the first entry.
199
- */
200
- if (Number.isInteger(workitem.maxInstances)) {
201
- const workitems = quest.goblin.getState().get('workitems');
202
- if (workitems) {
203
- const items = workitems.filter((v, k) =>
204
- k.startsWith(`${workitem.name}@`)
205
- );
206
-
207
- if (items.count() >= workitem.maxInstances) {
208
- const workitemId = items.keySeq().first();
209
- const workitemKind = workitems.get(workitemId).get('kind');
210
- //navigate to existing tab when possible
211
- if (navigate && workitemKind !== 'dialog') {
212
- yield desk.navToWorkitem({
213
- workitemId,
214
- });
215
- }
216
-
217
- quest.log.dbg(`Skipping ${widgetId} add`);
218
- return {
219
- desktopId,
220
- workitemId: widgetId,
221
- skipped: true,
222
- };
223
- }
224
- }
225
- }
226
-
227
- quest.dispatch('set-workitem', {
228
- id: widgetId,
229
- kind: workitem.kind,
230
- entityId: workitem.payload.entityId,
231
- context: workitem.contextId,
232
- view: workitem.view,
233
- name: workitem.name,
234
- description: workitem.description,
235
- glyph: workitem.icon,
236
- closable: true,
237
- });
238
-
239
- if (workitem.kind === 'dialog') {
240
- quest.dispatch('setCurrentDialogByContext', {
241
- contextId: workitem.contextId,
242
- workitemId: widgetId,
243
- });
244
- }
245
-
246
- if (navigate && workitem.kind !== 'dialog') {
247
- yield desk.navToWorkitem({
248
- workitemId: widgetId,
249
- });
250
- }
251
-
252
- return {
253
- desktopId,
254
- workitemId: widgetId,
255
- skipped: false,
256
- };
257
- });
258
-
259
- Goblin.registerQuest(goblinName, 'add-workitem', function* (
260
- quest,
261
- workitem,
262
- clientSessionId,
263
- navigate
264
- ) {
265
- if (!workitem.name) {
266
- throw new Error(
267
- `Cannot add workitem without a name: ${JSON.stringify(workitem)}`
268
- );
269
- }
270
-
271
- if (!workitem.payload) {
272
- workitem.payload = {};
273
- }
274
-
275
- if (workitem.newEntityType) {
276
- workitem.payload.entityId = `${workitem.newEntityType}@${quest.uuidV4()}`;
277
- }
278
-
279
- if (!workitem.id) {
280
- workitem.id = quest.uuidV4();
281
- }
282
-
283
- if (!workitem.view) {
284
- workitem.view = 'default';
285
- }
286
-
287
- if (!workitem.kind) {
288
- workitem.kind = 'tab';
289
- }
290
-
291
- if (workitem.kind === 'dialog') {
292
- workitem.maxInstances = 1;
293
- }
294
-
295
- if (!workitem.contextId) {
296
- const state = quest.goblin.getState();
297
- workitem.contextId = state.get(`current.workcontext`, null);
298
- }
299
-
300
- //Manage collision with desktopId
301
- if (workitem.payload) {
302
- if (workitem.payload.desktopId) {
303
- workitem.payload.deskId = workitem.payload.desktopId;
304
- delete workitem.payload.desktopId;
305
- }
306
- }
307
-
308
- if (!quest.user.canDo(`${workitem.name}.create`)) {
309
- return;
310
- }
311
-
312
- if (!workitem.mode) {
313
- //force readonly when adding a workitem without WORKSHOP_EDIT skills
314
- if (!quest.user.canDo(`${workitem.name}.edit`)) {
315
- workitem.mode = 'readonly';
316
- }
317
- }
318
-
319
- const desktopId = quest.goblin.id;
320
- const widgetId = `${workitem.name}@${desktopId}@${workitem.id}`;
321
-
322
- quest.log.dbg(`Adding ${widgetId}...`);
323
-
324
- yield desktopLock.lock(widgetId);
325
- quest.defer(() => desktopLock.unlock(widgetId));
326
-
327
- yield desktopLock.lock(desktopId);
328
- const res = yield doAdd(quest, widgetId, clientSessionId, workitem, navigate);
329
- desktopLock.unlock(desktopId);
330
-
331
- if (res.skipped) {
332
- quest.log.dbg(`Adding ${widgetId}...[FAILED]`);
333
- return null;
334
- } else {
335
- const workitemAPI = yield quest.create(
336
- widgetId,
337
- Object.assign(
338
- {
339
- id: widgetId,
340
- desktopId,
341
- clientSessionId,
342
- contextId: workitem.contextId,
343
- workflowId: workitem.workflowId,
344
- isDialog: workitem.kind === 'dialog',
345
- mode: workitem.mode ? workitem.mode : false,
346
- level: 1,
347
- },
348
- workitem.payload,
349
- {payload: workitem.payload}
350
- )
351
- );
352
-
353
- if (workitemAPI.waitLoaded) {
354
- yield workitemAPI.waitLoaded();
355
- }
356
- quest.log.dbg(`Adding ${widgetId}...[DONE]`);
357
- return widgetId;
358
- }
359
- });
360
-
361
- /******************************************************************************/
362
-
363
- Goblin.registerQuest(goblinName, 'add-context', function* (
364
- quest,
365
- contextId,
366
- name,
367
- scope
368
- ) {
369
- const contexts = quest.getAPI(`contexts@${quest.goblin.id}`);
370
- yield contexts.add({
371
- contextId,
372
- name,
373
- scope,
374
- });
375
- });
376
-
377
- /******************************************************************************/
378
-
379
- Goblin.registerQuest(goblinName, 'setHinter', function (quest, hinterId) {
380
- quest.do({hinterId});
381
- });
382
-
383
- Goblin.registerQuest(goblinName, 'setDetail', function (quest, hinterId) {
384
- if (!hinterId) {
385
- quest.do({detailId: null});
386
- return;
387
- }
388
- const detailId = `${hinterId.replace(`hinter`, `detail`)}`;
389
- quest.do({detailId});
390
- });
391
-
392
- /******************************************************************************/
393
-
394
- Goblin.registerQuest(goblinName, 'change-theme', function (quest, name) {
395
- quest.evt.full(`<${quest.goblin.id}>.change-theme.requested`, {
396
- name,
397
- });
398
- });
399
-
400
- /******************************************************************************/
401
-
402
- Goblin.registerQuest(goblinName, 'change-team', function (quest, teamId) {
403
- quest.do();
404
- });
405
-
406
- /******************************************************************************/
407
-
408
- Goblin.registerQuest(goblinName, 'get-current-context', function (quest) {
409
- return quest.goblin.getState().get('current.workcontext');
410
- });
411
-
412
- /******************************************************************************/
413
-
414
- Goblin.registerQuest(goblinName, 'set-nav-to-default', function* (
415
- quest,
416
- defaultContextId
417
- ) {
418
- const state = quest.goblin.getState();
419
- const currentWK = state.get('current.workcontext');
420
- if (!currentWK) {
421
- yield quest.me.navToContext({contextId: defaultContextId});
422
- }
423
- });
424
-
425
- Goblin.registerQuest(goblinName, 'navToContext', function (quest, contextId) {
426
- quest.do({contextId});
427
- });
428
-
429
- /******************************************************************************/
430
-
431
- Goblin.registerQuest(goblinName, 'navToWorkitem', function (quest, workitemId) {
432
- const state = quest.goblin.getState();
433
-
434
- const workitem = state.get(`workitems.${workitemId}`);
435
- if (!workitem) {
436
- quest.log.warn(
437
- `cancel navigate to an undefined workitem, ${workitemId} for desktop ${quest.goblin.id}`
438
- );
439
- return;
440
- }
441
-
442
- const contextId = workitem.get('context');
443
- const view = workitem.get('view');
444
-
445
- //set new current workitem
446
- quest.dispatch('setCurrentWorkitemByContext', {contextId, view, workitemId});
447
- });
448
-
449
- /******************************************************************************/
450
-
451
- Goblin.registerQuest(goblinName, 'navToLastWorkitem', function (quest) {
452
- const state = quest.goblin.getState();
453
- const currentWorkcontext = state.get('current.workcontext');
454
- const last = state.get(`last.${currentWorkcontext}`);
455
- if (!last) {
456
- return;
457
- }
458
-
459
- const workitemId = last.get('workitem');
460
-
461
- quest.dispatch('setCurrentWorkitemByContext', {
462
- contextId: currentWorkcontext,
463
- workitemId,
464
- });
465
- });
466
-
467
- /******************************************************************************/
468
-
469
- Goblin.registerQuest(goblinName, 'run-client-quest', function (
470
- quest,
471
- labId,
472
- goblinName,
473
- goblinId,
474
- questName,
475
- questArgs
476
- ) {
477
- quest.evt(`<${labId}>.run-client-quest-requested`, {
478
- desktopId: quest.goblin.id,
479
- goblinName,
480
- goblinId,
481
- questName,
482
- questArgs,
483
- });
484
- });
485
-
486
- /******************************************************************************/
487
-
488
- Goblin.registerQuest(goblinName, 'dispatch', function (quest, action) {
489
- quest.evt.full(`<${quest.goblin.id}>.dispatch.requested`, {
490
- action,
491
- });
492
- });
493
-
494
- Goblin.registerQuest(goblinName, 'start-nav', function* (quest) {
495
- const navigating = quest.goblin.getState().get('navigating');
496
- if (navigating) {
497
- return false;
498
- }
499
- yield quest.doSync();
500
- return true;
501
- });
502
-
503
- Goblin.registerQuest(goblinName, 'end-nav', function* (
504
- quest,
505
- navRequestId,
506
- route,
507
- skip
508
- ) {
509
- if (!skip) {
510
- yield quest.doSync();
511
- }
512
- if (navRequestId) {
513
- quest.evt(`${navRequestId}.done`);
514
- }
515
- });
516
-
517
- /******************************************************************************/
518
-
519
- Goblin.registerQuest(goblinName, 'gamepad-changed', function (
520
- quest,
521
- gamepad
522
- ) {});
523
-
524
- /******************************************************************************/
525
-
526
- //---------------//
527
- // Notifications //
528
- //---------------//
529
-
530
- Goblin.registerQuest(goblinName, 'read-notification', function (
531
- quest,
532
- notification
533
- ) {
534
- quest.do({notification});
535
- quest.dispatch('update-not-read-count');
536
- });
537
-
538
- Goblin.registerQuest(goblinName, 'add-notification', function (
539
- quest,
540
- notificationId,
541
- glyph,
542
- color,
543
- message,
544
- command,
545
- externalUrl,
546
- isDownload,
547
- broadcast,
548
- temporary,
549
- duration
550
- ) {
551
- if (!notificationId) {
552
- notificationId = quest.uuidV4();
553
- }
554
-
555
- message = StringBuilder.combine(message);
556
-
557
- if (broadcast) {
558
- quest.evt(
559
- `${quest.goblin.id.split('@')[1]}.desktop-notification-broadcasted`,
560
- {
561
- notificationId,
562
- glyph,
563
- color,
564
- message,
565
- command,
566
- externalUrl,
567
- isDownload,
568
- temporary,
569
- duration,
570
- }
571
- );
572
- return;
573
- }
574
-
575
- quest.do({
576
- notificationId,
577
- glyph,
578
- color,
579
- message,
580
- command,
581
- externalUrl,
582
- isDownload,
583
- temporary,
584
- duration,
585
- });
586
- const dnd = quest.goblin.getState().get('dnd');
587
- if (!dnd) {
588
- quest.dispatch('set-notifications', {show: true});
589
- }
590
- quest.dispatch('update-not-read-count');
591
-
592
- return quest.goblin
593
- .getState()
594
- .get(`notifications.${notificationId}`, null)
595
- .toJS();
596
- });
597
-
598
- Goblin.registerQuest(goblinName, 'remove-notification', function (
599
- quest,
600
- notification
601
- ) {
602
- quest.do({notification});
603
- quest.dispatch('update-not-read-count');
604
- });
605
-
606
- Goblin.registerQuest(goblinName, 'remove-notifications', function (quest) {
607
- quest.do();
608
- quest.dispatch('update-not-read-count');
609
- });
610
-
611
- Goblin.registerQuest(goblinName, 'click-notification', function* (
612
- quest,
613
- notification
614
- ) {
615
- if (notification.command) {
616
- yield quest.cmd(notification.command, {notification});
617
- }
618
- });
619
-
620
- Goblin.registerQuest(goblinName, 'set-dnd', function (quest, show) {
621
- quest.do();
622
- });
623
-
624
- Goblin.registerQuest(goblinName, 'set-only-news', function (quest, show) {
625
- quest.do();
626
- });
627
-
628
- Goblin.registerQuest(goblinName, 'set-notifications', function (quest, show) {
629
- quest.do();
630
- if (!show) {
631
- quest.dispatch('read-all');
632
- }
633
- quest.dispatch('update-not-read-count');
634
- });
635
-
636
- /******************************************************************************/
637
-
638
- //--------------//
639
- // StateMonitor //
640
- //--------------//
641
-
642
- Goblin.registerQuest(goblinName, 'show-state-monitor', function (quest) {
643
- quest.do();
644
- });
645
-
646
- Goblin.registerQuest(goblinName, 'add-state-monitor', function (quest) {
647
- quest.do();
648
- });
649
-
650
- Goblin.registerQuest(goblinName, 'back-state-monitor', function (quest) {
651
- quest.do();
652
- });
653
-
654
- Goblin.registerQuest(goblinName, 'forward-state-monitor', function (quest) {
655
- quest.do();
656
- });
657
-
658
- /******************************************************************************/
659
-
660
- Goblin.registerQuest(goblinName, 'download-file', function (
661
- quest,
662
- filePath,
663
- openFile,
664
- userContext
665
- ) {
666
- if (quest.user || userContext) {
667
- let clientSessionId, clientWindowId;
668
-
669
- if (userContext) {
670
- //use provided context
671
- const {sessionType, sessionId, windowId} = userContext;
672
- clientSessionId = `${sessionType}@${sessionId}`;
673
- clientWindowId = windowId;
674
- } else {
675
- //get context from current user
676
- const {sessionType, sessionId, windowId} = quest.user.getContext();
677
- clientSessionId = `${sessionType}@${sessionId}`;
678
- clientWindowId = windowId;
679
- }
680
-
681
- const fs = require('fs');
682
- const stream = fs.createReadStream;
683
- const routingKey = require('xcraft-core-host').getRoutingKey();
684
- if (fs.existsSync(filePath)) {
685
- const {getFileFilter} = require('xcraft-core-utils/lib/files.js');
686
- let file = stream(filePath);
687
- quest.evt(
688
- `<${clientSessionId}-${clientWindowId}-download-file-requested>`,
689
- {
690
- xcraftStream: file,
691
- routingKey,
692
- fileFilter: getFileFilter(filePath),
693
- defaultPath: path.basename(filePath),
694
- openFile,
695
- }
696
- );
697
- }
698
- } else {
699
- throw new Error('Cannot identifie user requesting download');
700
- }
701
- });
702
-
703
- /******************************************************************************/
704
-
705
- Goblin.registerQuest(goblinName, 'change-mandate', function (quest) {
706
- quest.evt(`mandate.changed`);
707
- });
708
-
709
- /******************************************************************************/
710
-
711
- Goblin.registerQuest(goblinName, 'change-screen', function (quest) {
712
- quest.evt(`screen.changed`);
713
- });
714
-
715
- /******************************************************************************/
716
-
717
- Goblin.registerQuest(goblinName, 'get-configuration', function (quest) {
718
- const conf = quest.goblin.getX('configuration');
719
- return conf;
720
- });
721
-
722
- /******************************************************************************/
723
-
724
- Goblin.registerQuest(goblinName, 'get-user-info', function (quest) {
725
- return quest.goblin.getState().get('username');
726
- });
727
-
728
- /******************************************************************************/
729
-
730
- Goblin.registerQuest(goblinName, 'get-lab-id', function (quest) {
731
- return quest.goblin.getX('labId');
732
- });
733
-
734
- /******************************************************************************/
735
-
736
- Goblin.registerQuest(goblinName, 'set-lab-id', function (quest, labId) {
737
- return quest.goblin.setX('labId', labId);
738
- });
739
-
740
- /******************************************************************************/
741
-
742
- Goblin.registerQuest(goblinName, 'get-client-session-id', function (quest) {
743
- return quest.goblin.getX('clientSessionId');
744
- });
745
-
746
- /******************************************************************************/
747
-
748
- Goblin.registerQuest(goblinName, 'get-workitems', function (quest) {
749
- const state = quest.goblin.getState();
750
- const wks = state.get('workitems');
751
- return wks ? wks.toJS() : {};
752
- });
753
-
754
- /******************************************************************************/
755
-
756
- Goblin.registerQuest(goblinName, 'close', function* (quest) {
757
- const deskManager = quest.getAPI('desktop-manager');
758
- yield deskManager.close({sessionDesktopId: quest.goblin.id});
759
- });
760
-
761
- Goblin.registerQuest(goblinName, 'closeCurrentTab', function* (quest) {
762
- const state = quest.goblin.getState();
763
- const context = state.get('current.workcontext');
764
- const currentWorkitem = state.get(`current.workitems.${context}`);
765
- const workitem = state.get(`workitems.${currentWorkitem}`);
766
- if (!workitem || workitem.get('kind') !== 'tab') {
767
- return;
768
- }
769
-
770
- yield quest.me.removeWorkitem({workitemId: currentWorkitem});
771
- });
772
-
773
- Goblin.registerQuest(goblinName, 'closeAllCurrentTabs', function* (quest) {
774
- const state = quest.goblin.getState();
775
- const context = state.get('current.workcontext');
776
-
777
- const toClose = [];
778
- for (const workitem of state.get(`workitems`).values()) {
779
- if (workitem.get('kind') === 'tab' && workitem.get('context') === context) {
780
- toClose.push(workitem.get('id'));
781
- }
782
- }
783
-
784
- for (const workitemId of toClose) {
785
- yield quest.me.removeWorkitem({workitemId});
786
- }
787
- });
788
-
789
- Goblin.registerQuest(goblinName, 'on-close-window', function (
790
- quest,
791
- currentUrl
792
- ) {
793
- quest.log.dbg(`${quest.goblin.id} window closing...`);
794
- });
795
-
796
- /******************************************************************************/
797
-
798
- Goblin.registerQuest(goblinName, 'open-entity-wizard', function* (quest) {
799
- yield quest.me.addWorkitem({
800
- workitem: {name: 'open-entity-wizard', kind: 'dialog'},
801
- });
802
- });
803
-
804
- /******************************************************************************/
805
-
806
- /******************************************************************************/
807
-
808
- Goblin.registerQuest(goblinName, 'save-note', function (quest, content) {
809
- quest.dispatch('set-note', {content});
810
- });
811
-
812
- /******************************************************************************/
813
-
814
- Goblin.registerQuest(goblinName, 'delete', function (quest) {
815
- quest.log.info('Deleting desktop...');
816
- });
817
-
818
- /******************************************************************************/
819
-
820
- Goblin.registerQuest(goblinName, 'toggle-monitor-feed', function* (
821
- quest,
822
- isOn
823
- ) {
824
- if (!isOn) {
825
- yield quest.warehouse.feedSubscriptionAdd({
826
- feed: quest.goblin.id,
827
- branch: 'activity-monitor',
828
- });
829
- } else {
830
- yield quest.warehouse.feedSubscriptionDel({
831
- feed: quest.goblin.id,
832
- branch: 'activity-monitor',
833
- });
834
- }
835
- });
836
-
837
- const getMetrics = function (goblin) {
838
- const metrics = {};
839
- const state = goblin.getState();
840
- const clientSession = goblin.getX('clientSessionId');
841
- const username = goblin.getState().get('username');
842
- const labels = {
843
- clientSession,
844
- username,
845
- };
846
- metrics['workitems'] = {labels, total: state.get('workitems').size};
847
- metrics['notifications'] = {labels, total: state.get('notifications').size};
848
- metrics['stateMonitorHistoryStack'] = {
849
- labels,
850
- total: state.get('stateMonitorHistory.stack').size,
851
- };
852
- return metrics;
853
- };
854
-
855
- // Create a Goblin with initial state and handlers
856
- module.exports = Goblin.configure(goblinName, logicState, logicHandlers, {
857
- getMetrics,
858
- });
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const Goblin = require('xcraft-core-goblin');
5
+ const watt = require('gigawatts');
6
+ const goblinName = path.basename(module.parent.filename, '.js');
7
+ const StringBuilder = require('goblin-nabu/lib/string-builder.js');
8
+ const locks = require('xcraft-core-utils/lib/locks.js');
9
+ const desktopLock = locks.getMutex;
10
+ // Define initial logic values
11
+ const logicState = {};
12
+
13
+ // Define logic handlers according rc.json
14
+ const logicHandlers = require('./logic-handlers.js');
15
+ /******************************************************************************/
16
+
17
+ // Register quest's according rc.json
18
+ Goblin.registerQuest(goblinName, 'create', function* (
19
+ quest,
20
+ clientSessionId,
21
+ labId,
22
+ username,
23
+ session,
24
+ configuration,
25
+ routes,
26
+ mainGoblin // FIXME: replace mainGoblin by mainGoblinModule
27
+ ) {
28
+ if (clientSessionId) {
29
+ quest.goblin.setX('clientSessionId', clientSessionId);
30
+ } else {
31
+ quest.log.warn('no clientSessionId provided to the new desktop');
32
+ }
33
+ quest.goblin.setX('labId', labId);
34
+ quest.goblin.setX('configuration', configuration);
35
+ // CREATE DEFAULT CONTEXT MANAGER
36
+ yield quest.create('contexts', {
37
+ id: `contexts@${quest.goblin.id}`,
38
+ desktopId: quest.goblin.id,
39
+ });
40
+
41
+ //CREATE TASKBAR (TASK LAUNCHER)
42
+ quest.create('taskbar', {
43
+ id: `taskbar@${quest.goblin.id}`,
44
+ desktopId: quest.goblin.id,
45
+ });
46
+
47
+ if (!mainGoblin && configuration) {
48
+ mainGoblin = configuration.mainGoblin;
49
+ }
50
+
51
+ if (mainGoblin) {
52
+ let useNabu = mainGoblin === 'nabu';
53
+
54
+ if (!useNabu) {
55
+ const mainConfig = require('xcraft-core-etc')().load(
56
+ `goblin-${mainGoblin}`
57
+ );
58
+ useNabu = mainConfig?.profile?.useNabu === true;
59
+ }
60
+
61
+ // CREATE NABU TOOLBAR IF NEEDED
62
+ if (useNabu) {
63
+ const toolbarId = `nabu-toolbar@${quest.goblin.id}`;
64
+ yield quest.create('nabu-toolbar', {
65
+ id: toolbarId,
66
+ desktopId: quest.goblin.id,
67
+ enabled: false,
68
+ show: true,
69
+ });
70
+ }
71
+ }
72
+
73
+ quest.do({
74
+ id: quest.goblin.id,
75
+ username,
76
+ session,
77
+ profileKey: configuration && configuration.id,
78
+ });
79
+
80
+ quest.log.info(() => `Desktop ${quest.goblin.id} created!`);
81
+ const id = quest.goblin.id;
82
+
83
+ quest.goblin.defer(
84
+ quest.sub(
85
+ `*::*.${quest.goblin.id.split('@')[1]}.desktop-notification-broadcasted`,
86
+ function* (err, {msg, resp}) {
87
+ yield resp.cmd(`${goblinName}.add-notification`, {id, ...msg.data});
88
+ }
89
+ )
90
+ );
91
+
92
+ quest.goblin.defer(
93
+ quest.sub(`*::*.${quest.goblin.id}.<add-workitem-requested>`, function* (
94
+ err,
95
+ {msg, resp}
96
+ ) {
97
+ yield resp.cmd(`${goblinName}.add-workitem`, {id, ...msg.data});
98
+ })
99
+ );
100
+
101
+ quest.goblin.defer(
102
+ quest.sub(`*::*.${quest.goblin.id}.<remove-workitem-requested>`, function* (
103
+ err,
104
+ {msg, resp}
105
+ ) {
106
+ yield resp.cmd(`${goblinName}.removeWorkitem`, {id, ...msg.data});
107
+ })
108
+ );
109
+
110
+ return quest.goblin.id;
111
+ });
112
+
113
+ /******************************************************************************/
114
+
115
+ Goblin.registerQuest(goblinName, 'change-locale', function (quest, locale) {
116
+ const labId = quest.goblin.getX('labId');
117
+ quest.evt(`<${labId}>.user-locale-changed`, {locale});
118
+ });
119
+
120
+ Goblin.registerQuest(goblinName, 'setDefaultNewWorkitem', function (
121
+ quest,
122
+ workitem
123
+ ) {
124
+ quest.goblin.setX('newWorkitemDef', workitem);
125
+ });
126
+
127
+ Goblin.registerQuest(goblinName, 'openNewWorkitem', function* (
128
+ quest,
129
+ clientSessionId
130
+ ) {
131
+ const workitem = quest.goblin.getX('newWorkitemDef');
132
+ if (!workitem) {
133
+ return;
134
+ }
135
+ yield quest.me.addWorkitem({
136
+ clientSessionId,
137
+ workitem: {...workitem}, //!newref
138
+ navigate: true,
139
+ });
140
+ });
141
+ /******************************************************************************/
142
+
143
+ Goblin.registerQuest(goblinName, 'removeWorkitem', function* (
144
+ quest,
145
+ workitemId,
146
+ close,
147
+ navToLastWorkitem
148
+ ) {
149
+ const state = quest.goblin.getState();
150
+ const workitem = state.get(`workitems.${workitemId}`);
151
+ if (!workitem) {
152
+ quest.log.dbg(`Skipping ${workitemId} remove...`);
153
+ return;
154
+ }
155
+
156
+ yield desktopLock.lock(workitemId);
157
+ quest.defer(() => desktopLock.unlock(workitemId));
158
+
159
+ yield quest.doSync({workitemId});
160
+
161
+ const api = quest.getAPI(workitemId);
162
+ if (api && close) {
163
+ if (api.close) {
164
+ yield api.close({kind: 'kill'});
165
+ }
166
+ }
167
+
168
+ yield quest.kill([workitemId]);
169
+
170
+ const kind = workitem.get('kind');
171
+ if (navToLastWorkitem && kind !== 'dialog') {
172
+ yield quest.me.navToLastWorkitem();
173
+ }
174
+
175
+ if (kind === 'dialog') {
176
+ const contextId = workitem.get('context');
177
+ quest.dispatch('setCurrentDialogByContext', {
178
+ contextId,
179
+ workitemId: null,
180
+ });
181
+ }
182
+ quest.log.dbg(`Removing ${workitemId}...[DONE]`);
183
+ });
184
+
185
+ /******************************************************************************/
186
+ const doAdd = watt(function* (
187
+ quest,
188
+ widgetId,
189
+ clientSessionId,
190
+ workitem,
191
+ navigate
192
+ ) {
193
+ const desk = quest.me;
194
+ const desktopId = desk.id;
195
+
196
+ /* Manage `maxInstances` property which is useful to limit the quantity
197
+ * of instances. If the `navigate` property is passed to true, then
198
+ * a navigate is performed with the first entry.
199
+ */
200
+ if (Number.isInteger(workitem.maxInstances)) {
201
+ const workitems = quest.goblin.getState().get('workitems');
202
+ if (workitems) {
203
+ const items = workitems.filter((v, k) =>
204
+ k.startsWith(`${workitem.name}@`)
205
+ );
206
+
207
+ if (items.count() >= workitem.maxInstances) {
208
+ const workitemId = items.keySeq().first();
209
+ const workitemKind = workitems.get(workitemId).get('kind');
210
+ //navigate to existing tab when possible
211
+ if (navigate && workitemKind !== 'dialog') {
212
+ yield desk.navToWorkitem({
213
+ workitemId,
214
+ });
215
+ }
216
+
217
+ quest.log.dbg(`Skipping ${widgetId} add`);
218
+ return {
219
+ desktopId,
220
+ workitemId: widgetId,
221
+ skipped: true,
222
+ };
223
+ }
224
+ }
225
+ }
226
+
227
+ quest.dispatch('set-workitem', {
228
+ id: widgetId,
229
+ kind: workitem.kind,
230
+ entityId: workitem.payload.entityId,
231
+ context: workitem.contextId,
232
+ view: workitem.view,
233
+ name: workitem.name,
234
+ description: workitem.description,
235
+ glyph: workitem.icon,
236
+ closable: true,
237
+ });
238
+
239
+ if (workitem.kind === 'dialog') {
240
+ quest.dispatch('setCurrentDialogByContext', {
241
+ contextId: workitem.contextId,
242
+ workitemId: widgetId,
243
+ });
244
+ }
245
+
246
+ if (navigate && workitem.kind !== 'dialog') {
247
+ yield desk.navToWorkitem({
248
+ workitemId: widgetId,
249
+ });
250
+ }
251
+
252
+ return {
253
+ desktopId,
254
+ workitemId: widgetId,
255
+ skipped: false,
256
+ };
257
+ });
258
+
259
+ Goblin.registerQuest(goblinName, 'add-workitem', function* (
260
+ quest,
261
+ workitem,
262
+ clientSessionId,
263
+ navigate
264
+ ) {
265
+ if (!workitem.name) {
266
+ throw new Error(
267
+ `Cannot add workitem without a name: ${JSON.stringify(workitem)}`
268
+ );
269
+ }
270
+
271
+ if (!workitem.payload) {
272
+ workitem.payload = {};
273
+ }
274
+
275
+ if (workitem.newEntityType) {
276
+ workitem.payload.entityId = `${workitem.newEntityType}@${quest.uuidV4()}`;
277
+ }
278
+
279
+ if (!workitem.id) {
280
+ workitem.id = quest.uuidV4();
281
+ }
282
+
283
+ if (!workitem.view) {
284
+ workitem.view = 'default';
285
+ }
286
+
287
+ if (!workitem.kind) {
288
+ workitem.kind = 'tab';
289
+ }
290
+
291
+ if (workitem.kind === 'dialog') {
292
+ workitem.maxInstances = 1;
293
+ }
294
+
295
+ if (!workitem.contextId) {
296
+ const state = quest.goblin.getState();
297
+ workitem.contextId = state.get(`current.workcontext`, null);
298
+ }
299
+
300
+ //Manage collision with desktopId
301
+ if (workitem.payload) {
302
+ if (workitem.payload.desktopId) {
303
+ workitem.payload.deskId = workitem.payload.desktopId;
304
+ delete workitem.payload.desktopId;
305
+ }
306
+ }
307
+
308
+ if (!quest.user.canDo(`${workitem.name}.create`)) {
309
+ return;
310
+ }
311
+
312
+ if (!workitem.mode) {
313
+ //force readonly when adding a workitem without WORKSHOP_EDIT skills
314
+ if (!quest.user.canDo(`${workitem.name}.edit`)) {
315
+ workitem.mode = 'readonly';
316
+ }
317
+ }
318
+
319
+ const desktopId = quest.goblin.id;
320
+ const widgetId = `${workitem.name}@${desktopId}@${workitem.id}`;
321
+
322
+ quest.log.dbg(`Adding ${widgetId}...`);
323
+
324
+ yield desktopLock.lock(widgetId);
325
+ quest.defer(() => desktopLock.unlock(widgetId));
326
+
327
+ yield desktopLock.lock(desktopId);
328
+ const res = yield doAdd(quest, widgetId, clientSessionId, workitem, navigate);
329
+ desktopLock.unlock(desktopId);
330
+
331
+ if (res.skipped) {
332
+ quest.log.dbg(`Adding ${widgetId}...[FAILED]`);
333
+ return null;
334
+ } else {
335
+ const workitemAPI = yield quest.create(
336
+ widgetId,
337
+ Object.assign(
338
+ {
339
+ id: widgetId,
340
+ desktopId,
341
+ clientSessionId,
342
+ contextId: workitem.contextId,
343
+ workflowId: workitem.workflowId,
344
+ isDialog: workitem.kind === 'dialog',
345
+ mode: workitem.mode ? workitem.mode : false,
346
+ level: 1,
347
+ },
348
+ workitem.payload,
349
+ {payload: workitem.payload}
350
+ )
351
+ );
352
+
353
+ if (workitemAPI.waitLoaded) {
354
+ yield workitemAPI.waitLoaded();
355
+ }
356
+ quest.log.dbg(`Adding ${widgetId}...[DONE]`);
357
+ return widgetId;
358
+ }
359
+ });
360
+
361
+ /******************************************************************************/
362
+
363
+ Goblin.registerQuest(goblinName, 'add-context', function* (
364
+ quest,
365
+ contextId,
366
+ name,
367
+ scope
368
+ ) {
369
+ const contexts = quest.getAPI(`contexts@${quest.goblin.id}`);
370
+ yield contexts.add({
371
+ contextId,
372
+ name,
373
+ scope,
374
+ });
375
+ });
376
+
377
+ /******************************************************************************/
378
+
379
+ Goblin.registerQuest(goblinName, 'setHinter', function (quest, hinterId) {
380
+ quest.do({hinterId});
381
+ });
382
+
383
+ Goblin.registerQuest(goblinName, 'setDetail', function (quest, hinterId) {
384
+ if (!hinterId) {
385
+ quest.do({detailId: null});
386
+ return;
387
+ }
388
+ const detailId = `${hinterId.replace(`hinter`, `detail`)}`;
389
+ quest.do({detailId});
390
+ });
391
+
392
+ /******************************************************************************/
393
+
394
+ Goblin.registerQuest(goblinName, 'change-theme', function (quest, name) {
395
+ quest.evt.full(`<${quest.goblin.id}>.change-theme.requested`, {
396
+ name,
397
+ });
398
+ });
399
+
400
+ /******************************************************************************/
401
+
402
+ Goblin.registerQuest(goblinName, 'change-team', function (quest, teamId) {
403
+ quest.do();
404
+ });
405
+
406
+ /******************************************************************************/
407
+
408
+ Goblin.registerQuest(goblinName, 'get-current-context', function (quest) {
409
+ return quest.goblin.getState().get('current.workcontext');
410
+ });
411
+
412
+ /******************************************************************************/
413
+
414
+ Goblin.registerQuest(goblinName, 'set-nav-to-default', function* (
415
+ quest,
416
+ defaultContextId
417
+ ) {
418
+ const state = quest.goblin.getState();
419
+ const currentWK = state.get('current.workcontext');
420
+ if (!currentWK) {
421
+ yield quest.me.navToContext({contextId: defaultContextId});
422
+ }
423
+ });
424
+
425
+ Goblin.registerQuest(goblinName, 'navToContext', function (quest, contextId) {
426
+ quest.do({contextId});
427
+ });
428
+
429
+ /******************************************************************************/
430
+
431
+ Goblin.registerQuest(goblinName, 'navToWorkitem', function (quest, workitemId) {
432
+ const state = quest.goblin.getState();
433
+
434
+ const workitem = state.get(`workitems.${workitemId}`);
435
+ if (!workitem) {
436
+ quest.log.warn(
437
+ `cancel navigate to an undefined workitem, ${workitemId} for desktop ${quest.goblin.id}`
438
+ );
439
+ return;
440
+ }
441
+
442
+ const contextId = workitem.get('context');
443
+ const view = workitem.get('view');
444
+
445
+ //set new current workitem
446
+ quest.dispatch('setCurrentWorkitemByContext', {contextId, view, workitemId});
447
+ });
448
+
449
+ /******************************************************************************/
450
+
451
+ Goblin.registerQuest(goblinName, 'navToLastWorkitem', function (quest) {
452
+ const state = quest.goblin.getState();
453
+ const currentWorkcontext = state.get('current.workcontext');
454
+ const last = state.get(`last.${currentWorkcontext}`);
455
+ if (!last) {
456
+ return;
457
+ }
458
+
459
+ const workitemId = last.get('workitem');
460
+
461
+ quest.dispatch('setCurrentWorkitemByContext', {
462
+ contextId: currentWorkcontext,
463
+ workitemId,
464
+ });
465
+ });
466
+
467
+ /******************************************************************************/
468
+
469
+ Goblin.registerQuest(goblinName, 'run-client-quest', function (
470
+ quest,
471
+ labId,
472
+ goblinName,
473
+ goblinId,
474
+ questName,
475
+ questArgs
476
+ ) {
477
+ quest.evt(`<${labId}>.run-client-quest-requested`, {
478
+ desktopId: quest.goblin.id,
479
+ goblinName,
480
+ goblinId,
481
+ questName,
482
+ questArgs,
483
+ });
484
+ });
485
+
486
+ /******************************************************************************/
487
+
488
+ Goblin.registerQuest(goblinName, 'dispatch', function (quest, action) {
489
+ quest.evt.full(`<${quest.goblin.id}>.dispatch.requested`, {
490
+ action,
491
+ });
492
+ });
493
+
494
+ Goblin.registerQuest(goblinName, 'start-nav', function* (quest) {
495
+ const navigating = quest.goblin.getState().get('navigating');
496
+ if (navigating) {
497
+ return false;
498
+ }
499
+ yield quest.doSync();
500
+ return true;
501
+ });
502
+
503
+ Goblin.registerQuest(goblinName, 'end-nav', function* (
504
+ quest,
505
+ navRequestId,
506
+ route,
507
+ skip
508
+ ) {
509
+ if (!skip) {
510
+ yield quest.doSync();
511
+ }
512
+ if (navRequestId) {
513
+ quest.evt(`${navRequestId}.done`);
514
+ }
515
+ });
516
+
517
+ /******************************************************************************/
518
+
519
+ Goblin.registerQuest(goblinName, 'gamepad-changed', function (
520
+ quest,
521
+ gamepad
522
+ ) {});
523
+
524
+ /******************************************************************************/
525
+
526
+ //---------------//
527
+ // Notifications //
528
+ //---------------//
529
+
530
+ Goblin.registerQuest(goblinName, 'read-notification', function (
531
+ quest,
532
+ notification
533
+ ) {
534
+ quest.do({notification});
535
+ quest.dispatch('update-not-read-count');
536
+ });
537
+
538
+ Goblin.registerQuest(goblinName, 'add-notification', function (
539
+ quest,
540
+ notificationId,
541
+ glyph,
542
+ color,
543
+ message,
544
+ command,
545
+ externalUrl,
546
+ isDownload,
547
+ broadcast,
548
+ temporary,
549
+ duration
550
+ ) {
551
+ if (!notificationId) {
552
+ notificationId = quest.uuidV4();
553
+ }
554
+
555
+ message = StringBuilder.combine(message);
556
+
557
+ if (broadcast) {
558
+ quest.evt(
559
+ `${quest.goblin.id.split('@')[1]}.desktop-notification-broadcasted`,
560
+ {
561
+ notificationId,
562
+ glyph,
563
+ color,
564
+ message,
565
+ command,
566
+ externalUrl,
567
+ isDownload,
568
+ temporary,
569
+ duration,
570
+ }
571
+ );
572
+ return;
573
+ }
574
+
575
+ quest.do({
576
+ notificationId,
577
+ glyph,
578
+ color,
579
+ message,
580
+ command,
581
+ externalUrl,
582
+ isDownload,
583
+ temporary,
584
+ duration,
585
+ });
586
+ const dnd = quest.goblin.getState().get('dnd');
587
+ if (!dnd) {
588
+ quest.dispatch('set-notifications', {show: true});
589
+ }
590
+ quest.dispatch('update-not-read-count');
591
+
592
+ return quest.goblin
593
+ .getState()
594
+ .get(`notifications.${notificationId}`, null)
595
+ .toJS();
596
+ });
597
+
598
+ Goblin.registerQuest(goblinName, 'remove-notification', function (
599
+ quest,
600
+ notification
601
+ ) {
602
+ quest.do({notification});
603
+ quest.dispatch('update-not-read-count');
604
+ });
605
+
606
+ Goblin.registerQuest(goblinName, 'remove-notifications', function (quest) {
607
+ quest.do();
608
+ quest.dispatch('update-not-read-count');
609
+ });
610
+
611
+ Goblin.registerQuest(goblinName, 'click-notification', function* (
612
+ quest,
613
+ notification
614
+ ) {
615
+ if (notification.command) {
616
+ yield quest.cmd(notification.command, {notification});
617
+ }
618
+ });
619
+
620
+ Goblin.registerQuest(goblinName, 'set-dnd', function (quest, show) {
621
+ quest.do();
622
+ });
623
+
624
+ Goblin.registerQuest(goblinName, 'set-only-news', function (quest, show) {
625
+ quest.do();
626
+ });
627
+
628
+ Goblin.registerQuest(goblinName, 'set-notifications', function (quest, show) {
629
+ quest.do();
630
+ if (!show) {
631
+ quest.dispatch('read-all');
632
+ }
633
+ quest.dispatch('update-not-read-count');
634
+ });
635
+
636
+ /******************************************************************************/
637
+
638
+ //--------------//
639
+ // StateMonitor //
640
+ //--------------//
641
+
642
+ Goblin.registerQuest(goblinName, 'show-state-monitor', function (quest) {
643
+ quest.do();
644
+ });
645
+
646
+ Goblin.registerQuest(goblinName, 'add-state-monitor', function (quest) {
647
+ quest.do();
648
+ });
649
+
650
+ Goblin.registerQuest(goblinName, 'back-state-monitor', function (quest) {
651
+ quest.do();
652
+ });
653
+
654
+ Goblin.registerQuest(goblinName, 'forward-state-monitor', function (quest) {
655
+ quest.do();
656
+ });
657
+
658
+ /******************************************************************************/
659
+
660
+ Goblin.registerQuest(goblinName, 'download-file', function (
661
+ quest,
662
+ filePath,
663
+ openFile,
664
+ userContext
665
+ ) {
666
+ if (quest.user || userContext) {
667
+ let clientSessionId, clientWindowId;
668
+
669
+ if (userContext) {
670
+ //use provided context
671
+ const {sessionType, sessionId, windowId} = userContext;
672
+ clientSessionId = `${sessionType}@${sessionId}`;
673
+ clientWindowId = windowId;
674
+ } else {
675
+ //get context from current user
676
+ const {sessionType, sessionId, windowId} = quest.user.getContext();
677
+ clientSessionId = `${sessionType}@${sessionId}`;
678
+ clientWindowId = windowId;
679
+ }
680
+
681
+ const fs = require('fs');
682
+ const stream = fs.createReadStream;
683
+ const routingKey = require('xcraft-core-host').getRoutingKey();
684
+ if (fs.existsSync(filePath)) {
685
+ const {getFileFilter} = require('xcraft-core-utils/lib/files.js');
686
+ let file = stream(filePath);
687
+ quest.evt(
688
+ `<${clientSessionId}-${clientWindowId}-download-file-requested>`,
689
+ {
690
+ xcraftStream: file,
691
+ routingKey,
692
+ fileFilter: getFileFilter(filePath),
693
+ defaultPath: path.basename(filePath),
694
+ openFile,
695
+ }
696
+ );
697
+ }
698
+ } else {
699
+ throw new Error('Cannot identifie user requesting download');
700
+ }
701
+ });
702
+
703
+ /******************************************************************************/
704
+
705
+ Goblin.registerQuest(goblinName, 'change-mandate', function (quest) {
706
+ quest.evt(`mandate.changed`);
707
+ });
708
+
709
+ /******************************************************************************/
710
+
711
+ Goblin.registerQuest(goblinName, 'change-screen', function (quest) {
712
+ quest.evt(`screen.changed`);
713
+ });
714
+
715
+ /******************************************************************************/
716
+
717
+ Goblin.registerQuest(goblinName, 'get-configuration', function (quest) {
718
+ const conf = quest.goblin.getX('configuration');
719
+ return conf;
720
+ });
721
+
722
+ /******************************************************************************/
723
+
724
+ Goblin.registerQuest(goblinName, 'get-user-info', function (quest) {
725
+ return quest.goblin.getState().get('username');
726
+ });
727
+
728
+ /******************************************************************************/
729
+
730
+ Goblin.registerQuest(goblinName, 'get-lab-id', function (quest) {
731
+ return quest.goblin.getX('labId');
732
+ });
733
+
734
+ /******************************************************************************/
735
+
736
+ Goblin.registerQuest(goblinName, 'set-lab-id', function (quest, labId) {
737
+ return quest.goblin.setX('labId', labId);
738
+ });
739
+
740
+ /******************************************************************************/
741
+
742
+ Goblin.registerQuest(goblinName, 'get-client-session-id', function (quest) {
743
+ return quest.goblin.getX('clientSessionId');
744
+ });
745
+
746
+ /******************************************************************************/
747
+
748
+ Goblin.registerQuest(goblinName, 'get-workitems', function (quest) {
749
+ const state = quest.goblin.getState();
750
+ const wks = state.get('workitems');
751
+ return wks ? wks.toJS() : {};
752
+ });
753
+
754
+ /******************************************************************************/
755
+
756
+ Goblin.registerQuest(goblinName, 'close', function* (quest) {
757
+ const deskManager = quest.getAPI('desktop-manager');
758
+ yield deskManager.close({sessionDesktopId: quest.goblin.id});
759
+ });
760
+
761
+ Goblin.registerQuest(goblinName, 'closeCurrentTab', function* (quest) {
762
+ const state = quest.goblin.getState();
763
+ const context = state.get('current.workcontext');
764
+ const currentWorkitem = state.get(`current.workitems.${context}`);
765
+ const workitem = state.get(`workitems.${currentWorkitem}`);
766
+ if (!workitem || workitem.get('kind') !== 'tab') {
767
+ return;
768
+ }
769
+
770
+ yield quest.me.removeWorkitem({workitemId: currentWorkitem});
771
+ });
772
+
773
+ Goblin.registerQuest(goblinName, 'closeAllCurrentTabs', function* (quest) {
774
+ const state = quest.goblin.getState();
775
+ const context = state.get('current.workcontext');
776
+
777
+ const toClose = [];
778
+ for (const workitem of state.get(`workitems`).values()) {
779
+ if (workitem.get('kind') === 'tab' && workitem.get('context') === context) {
780
+ toClose.push(workitem.get('id'));
781
+ }
782
+ }
783
+
784
+ for (const workitemId of toClose) {
785
+ yield quest.me.removeWorkitem({workitemId});
786
+ }
787
+ });
788
+
789
+ Goblin.registerQuest(goblinName, 'on-close-window', function (
790
+ quest,
791
+ currentUrl
792
+ ) {
793
+ quest.log.dbg(`${quest.goblin.id} window closing...`);
794
+ });
795
+
796
+ /******************************************************************************/
797
+
798
+ Goblin.registerQuest(goblinName, 'open-entity-wizard', function* (quest) {
799
+ yield quest.me.addWorkitem({
800
+ workitem: {name: 'open-entity-wizard', kind: 'dialog'},
801
+ });
802
+ });
803
+
804
+ /******************************************************************************/
805
+
806
+ /******************************************************************************/
807
+
808
+ Goblin.registerQuest(goblinName, 'save-note', function (quest, content) {
809
+ quest.dispatch('set-note', {content});
810
+ });
811
+
812
+ /******************************************************************************/
813
+
814
+ Goblin.registerQuest(goblinName, 'delete', function (quest) {
815
+ quest.log.info('Deleting desktop...');
816
+ });
817
+
818
+ /******************************************************************************/
819
+
820
+ Goblin.registerQuest(goblinName, 'toggle-monitor-feed', function* (
821
+ quest,
822
+ isOn
823
+ ) {
824
+ if (!isOn) {
825
+ yield quest.warehouse.feedSubscriptionAdd({
826
+ feed: quest.goblin.id,
827
+ branch: 'activity-monitor',
828
+ });
829
+ } else {
830
+ yield quest.warehouse.feedSubscriptionDel({
831
+ feed: quest.goblin.id,
832
+ branch: 'activity-monitor',
833
+ });
834
+ }
835
+ });
836
+
837
+ const getMetrics = function (goblin) {
838
+ const metrics = {};
839
+ const state = goblin.getState();
840
+ const clientSession = goblin.getX('clientSessionId');
841
+ const username = goblin.getState().get('username');
842
+ const labels = {
843
+ clientSession,
844
+ username,
845
+ };
846
+ metrics['workitems'] = {labels, total: state.get('workitems').size};
847
+ metrics['notifications'] = {labels, total: state.get('notifications').size};
848
+ metrics['stateMonitorHistoryStack'] = {
849
+ labels,
850
+ total: state.get('stateMonitorHistory.stack').size,
851
+ };
852
+ return metrics;
853
+ };
854
+
855
+ // Create a Goblin with initial state and handlers
856
+ module.exports = Goblin.configure(goblinName, logicState, logicHandlers, {
857
+ getMetrics,
858
+ });