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