@genesislcap/pbc-notify-ui 15.8.3 → 15.9.0

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 (102) hide show
  1. package/README.md +39 -0
  2. package/custom-elements-manifest.config.js +5 -0
  3. package/dist/custom-elements.json +1771 -321
  4. package/dist/dts/components/components.d.ts.map +1 -1
  5. package/dist/dts/components/foundation-inbox/inbox-base/inbox-base.d.ts +2 -0
  6. package/dist/dts/components/foundation-inbox/inbox-base/inbox-base.d.ts.map +1 -1
  7. package/dist/dts/components/foundation-inbox/inbox.styles.d.ts.map +1 -1
  8. package/dist/dts/components/foundation-inbox/inbox.template.d.ts.map +1 -1
  9. package/dist/dts/components/foundation-inbox/inbox.types.d.ts +4 -1
  10. package/dist/dts/components/foundation-inbox/inbox.types.d.ts.map +1 -1
  11. package/dist/dts/components/foundation-notification-dashboard/alert-sound-attachment.d.ts +86 -0
  12. package/dist/dts/components/foundation-notification-dashboard/alert-sound-attachment.d.ts.map +1 -0
  13. package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.d.ts +39 -1
  14. package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.d.ts.map +1 -1
  15. package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.template.d.ts.map +1 -1
  16. package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types.d.ts +8 -0
  17. package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types.d.ts.map +1 -1
  18. package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-management.d.ts.map +1 -1
  19. package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.d.ts +41 -1
  20. package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.d.ts.map +1 -1
  21. package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.template.d.ts.map +1 -1
  22. package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.types.d.ts +2 -1
  23. package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.types.d.ts.map +1 -1
  24. package/dist/dts/components/foundation-notification-dashboard/components/templates/template-management.d.ts.map +1 -1
  25. package/dist/dts/components/foundation-notification-dashboard/styles/dynamic-rule.styles.d.ts.map +1 -1
  26. package/dist/dts/components/foundation-sounds/foundation-sounds-panel.d.ts +73 -0
  27. package/dist/dts/components/foundation-sounds/foundation-sounds-panel.d.ts.map +1 -0
  28. package/dist/dts/components/foundation-sounds/foundation-sounds-panel.styles.d.ts +2 -0
  29. package/dist/dts/components/foundation-sounds/foundation-sounds-panel.styles.d.ts.map +1 -0
  30. package/dist/dts/components/foundation-sounds/foundation-sounds-panel.template.d.ts +3 -0
  31. package/dist/dts/components/foundation-sounds/foundation-sounds-panel.template.d.ts.map +1 -0
  32. package/dist/dts/components/foundation-sounds/foundation-sounds.d.ts +34 -0
  33. package/dist/dts/components/foundation-sounds/foundation-sounds.d.ts.map +1 -0
  34. package/dist/dts/components/foundation-sounds/foundation-sounds.styles.d.ts +3 -0
  35. package/dist/dts/components/foundation-sounds/foundation-sounds.styles.d.ts.map +1 -0
  36. package/dist/dts/index.d.ts +4 -0
  37. package/dist/dts/index.d.ts.map +1 -1
  38. package/dist/dts/react.d.ts +31 -11
  39. package/dist/dts/services/inbox.service.d.ts +6 -0
  40. package/dist/dts/services/inbox.service.d.ts.map +1 -1
  41. package/dist/dts/services/rule.service.d.ts +2 -1
  42. package/dist/dts/services/rule.service.d.ts.map +1 -1
  43. package/dist/dts/utils/alert-sound.d.ts +177 -0
  44. package/dist/dts/utils/alert-sound.d.ts.map +1 -0
  45. package/dist/dts/utils/alert-sound.test.d.ts +2 -0
  46. package/dist/dts/utils/alert-sound.test.d.ts.map +1 -0
  47. package/dist/esm/components/components.js +4 -0
  48. package/dist/esm/components/foundation-inbox/inbox-base/inbox-base.js +6 -0
  49. package/dist/esm/components/foundation-inbox/inbox.styles.js +15 -0
  50. package/dist/esm/components/foundation-inbox/inbox.template.js +16 -0
  51. package/dist/esm/components/foundation-inbox/inbox.types.js +1 -0
  52. package/dist/esm/components/foundation-notification-dashboard/alert-sound-attachment.js +218 -0
  53. package/dist/esm/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.js +17 -2
  54. package/dist/esm/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.template.js +3 -0
  55. package/dist/esm/components/foundation-notification-dashboard/components/rules/rule-management.js +5 -1
  56. package/dist/esm/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.js +23 -2
  57. package/dist/esm/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.template.js +3 -0
  58. package/dist/esm/components/foundation-notification-dashboard/components/templates/template-management.js +8 -1
  59. package/dist/esm/components/foundation-notification-dashboard/styles/dynamic-rule.styles.js +31 -1
  60. package/dist/esm/components/foundation-sounds/foundation-sounds-panel.js +283 -0
  61. package/dist/esm/components/foundation-sounds/foundation-sounds-panel.styles.js +200 -0
  62. package/dist/esm/components/foundation-sounds/foundation-sounds-panel.template.js +179 -0
  63. package/dist/esm/components/foundation-sounds/foundation-sounds.js +206 -0
  64. package/dist/esm/components/foundation-sounds/foundation-sounds.styles.js +7 -0
  65. package/dist/esm/index.js +3 -0
  66. package/dist/esm/services/inbox.service.js +6 -2
  67. package/dist/esm/services/rule.service.js +35 -0
  68. package/dist/esm/utils/alert-sound.js +955 -0
  69. package/dist/esm/utils/alert-sound.test.js +25 -0
  70. package/dist/esm/utils/index.js +3 -0
  71. package/dist/pbc-notify-ui.api.json +2859 -332
  72. package/dist/pbc-notify-ui.d.ts +281 -3
  73. package/dist/react.cjs +19 -5
  74. package/dist/react.mjs +16 -4
  75. package/dist/tsconfig.tsbuildinfo +1 -1
  76. package/package.json +23 -23
  77. package/src/components/components.ts +4 -0
  78. package/src/components/foundation-inbox/inbox-base/inbox-base.ts +7 -0
  79. package/src/components/foundation-inbox/inbox.styles.ts +15 -0
  80. package/src/components/foundation-inbox/inbox.template.ts +16 -0
  81. package/src/components/foundation-inbox/inbox.types.ts +3 -0
  82. package/src/components/foundation-notification-dashboard/alert-sound-attachment.ts +283 -0
  83. package/src/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.template.ts +3 -0
  84. package/src/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.ts +21 -2
  85. package/src/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types.ts +9 -0
  86. package/src/components/foundation-notification-dashboard/components/rules/rule-management.ts +5 -1
  87. package/src/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.template.ts +3 -0
  88. package/src/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.ts +25 -2
  89. package/src/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.types.ts +2 -1
  90. package/src/components/foundation-notification-dashboard/components/templates/template-management.ts +8 -1
  91. package/src/components/foundation-notification-dashboard/styles/dynamic-rule.styles.ts +31 -1
  92. package/src/components/foundation-sounds/foundation-sounds-panel.styles.ts +201 -0
  93. package/src/components/foundation-sounds/foundation-sounds-panel.template.ts +188 -0
  94. package/src/components/foundation-sounds/foundation-sounds-panel.ts +280 -0
  95. package/src/components/foundation-sounds/foundation-sounds.styles.ts +8 -0
  96. package/src/components/foundation-sounds/foundation-sounds.ts +222 -0
  97. package/src/index.ts +8 -0
  98. package/src/services/inbox.service.ts +12 -2
  99. package/src/services/rule.service.ts +37 -1
  100. package/src/utils/alert-sound.test.ts +32 -0
  101. package/src/utils/alert-sound.ts +1135 -0
  102. package/src/utils/index.ts +3 -0
@@ -5,6 +5,7 @@ import { NotifyService } from '../../../../../services/notify.service';
5
5
  import { RuleService } from '../../../../../services/rule.service';
6
6
  import { SystemService } from '../../../../../services/system.service';
7
7
  import { humanize } from '../../../../../utils';
8
+ import { AlertSoundAttachmentMixin } from '../../../alert-sound-attachment';
8
9
  import { DynamicRuleUtils, RuleUtils, showNotificationError, isEmpty, } from '../../../notification-dashboard.utils';
9
10
  import { DynamicRuleDialogStyles } from '../../../styles/dynamic-rule.styles';
10
11
  import { RuleDialogTemplate } from './rule-dialog.template';
@@ -15,7 +16,7 @@ import { RuleDialogMode } from './rule-dialog.types';
15
16
  *
16
17
  * @fires close - Fired when the dialog should close
17
18
  */
18
- let RuleDialog = class RuleDialog extends GenesisElement {
19
+ let RuleDialog = class RuleDialog extends AlertSoundAttachmentMixin(GenesisElement) {
19
20
  constructor() {
20
21
  super(...arguments);
21
22
  this.ruleId = null;
@@ -36,6 +37,15 @@ let RuleDialog = class RuleDialog extends GenesisElement {
36
37
  this.fields = [];
37
38
  this.topics = [];
38
39
  }
40
+ get isAlertSoundEditMode() {
41
+ return this.ruleDialogMode === RuleDialogMode.EDIT;
42
+ }
43
+ get alertSoundEntityId() {
44
+ return this.ruleId;
45
+ }
46
+ get alertSoundCacheSyncMode() {
47
+ return 'entity';
48
+ }
39
49
  openDialog(params) {
40
50
  return __awaiter(this, void 0, void 0, function* () {
41
51
  this.topics = yield this.notifyService.getNotifyRouteTopics();
@@ -55,6 +65,7 @@ let RuleDialog = class RuleDialog extends GenesisElement {
55
65
  });
56
66
  }
57
67
  close() {
68
+ this.stopTestSound();
58
69
  // Objects
59
70
  this.ruleId = null;
60
71
  this.name = null;
@@ -65,6 +76,7 @@ let RuleDialog = class RuleDialog extends GenesisElement {
65
76
  this.topic = null;
66
77
  this.severity = null;
67
78
  this.ruleDialogMode = null;
79
+ this.resetAlertSoundState();
68
80
  // Arrays
69
81
  this.resources = [];
70
82
  this.topics = [];
@@ -107,6 +119,7 @@ let RuleDialog = class RuleDialog extends GenesisElement {
107
119
  this.conditions = DynamicRuleUtils.resolveLeftValues(loadedConditions, this.fields);
108
120
  this.refreshExpressionPreview();
109
121
  this.attributeAssignments((_c = rule.RESULT_EXPRESSION) === null || _c === void 0 ? void 0 : _c.ASSIGNMENTS);
122
+ yield this.loadExistingAlertSound(rule.DYNAMIC_RULE_ID);
110
123
  });
111
124
  }
112
125
  refreshExpressionPreview() {
@@ -151,7 +164,7 @@ let RuleDialog = class RuleDialog extends GenesisElement {
151
164
  DynamicRuleUtils.hasOrJoin(this.conditions) ||
152
165
  DynamicRuleUtils.hasGroup(this.conditions) ||
153
166
  DynamicRuleUtils.hasUnsupportedOperator(this.conditions);
154
- return Object.assign(Object.assign({}, (this.ruleDialogMode === RuleDialogMode.EDIT && { DYNAMIC_RULE_ID: this.ruleId })), { RULE_NAME: this.name, RULE_DESCRIPTION: this.description, RULE_TABLE: this.resource, TABLE_OPERATIONS: this.updateType, RULE_EXPRESSION: useRaw
167
+ const rule = Object.assign(Object.assign({}, (this.ruleDialogMode === RuleDialogMode.EDIT && { DYNAMIC_RULE_ID: this.ruleId })), { RULE_NAME: this.name, RULE_DESCRIPTION: this.description, RULE_TABLE: this.resource, TABLE_OPERATIONS: this.updateType, RULE_EXPRESSION: useRaw
155
168
  ? null
156
169
  : {
157
170
  CONDITIONS: DynamicRuleUtils.createConditions(this.conditions),
@@ -163,9 +176,11 @@ let RuleDialog = class RuleDialog extends GenesisElement {
163
176
  DynamicRuleUtils.createAssignment(Assignment.NOTIFY_SEVERITY, humanize(this.severity)),
164
177
  ],
165
178
  } });
179
+ return this.applyAlertSoundToPayload(rule);
166
180
  }
167
181
  validateResponse(response) {
168
182
  if (response.MESSAGE_TYPE === 'EVENT_ACK') {
183
+ this.syncAlertSoundCacheAfterSave();
169
184
  this.close();
170
185
  return;
171
186
  }
@@ -2,6 +2,7 @@ import { sync } from '@genesislcap/foundation-utils';
2
2
  import { html, ref, repeat, when } from '@genesislcap/web-core';
3
3
  import { Severity, UpdateType } from '../../../../../notify.types';
4
4
  import { eventDetail } from '../../../../../utils/eventDetail';
5
+ import { alertSoundAttachmentTemplate } from '../../../alert-sound-attachment';
5
6
  const isGroup = (node) => Array.isArray(node.CHILDREN);
6
7
  export const RuleDialogTemplate = html `
7
8
  <rapid-modal ${ref('dialog')} class="dialog" :onCloseCallback=${(x) => () => x.close()}>
@@ -115,6 +116,8 @@ export const RuleDialogTemplate = html `
115
116
  </div>
116
117
  </div>
117
118
 
119
+ ${alertSoundAttachmentTemplate}
120
+
118
121
  <div class="control">
119
122
  <label>Condition(s)</label>
120
123
  <div class="items">
@@ -2,6 +2,7 @@ import { __awaiter, __decorate } from "tslib";
2
2
  import { showNotificationDialog } from '@genesislcap/foundation-notifications';
3
3
  import { customElement, GenesisElement, html, ref, repeat } from '@genesislcap/web-core';
4
4
  import { RuleService } from '../../../../services/rule.service';
5
+ import { AlertSoundPlayer } from '../../../../utils/alert-sound';
5
6
  import { DynamicRuleManagementStyles } from '../../styles/dynamic-rule.styles';
6
7
  import { RULE_MANAGEMENT_COLUMNS } from './columns';
7
8
  import { RuleDialogMode } from './rule-dialog/rule-dialog.types';
@@ -27,7 +28,10 @@ let RuleManagement = class RuleManagement extends GenesisElement {
27
28
  confirmingActions: [
28
29
  {
29
30
  label: 'Confirm',
30
- action: () => this.ruleService.deleteRule(rule.ID),
31
+ action: () => {
32
+ AlertSoundPlayer.shared.clearServerSound(rule.ID);
33
+ this.ruleService.deleteRule(rule.ID);
34
+ },
31
35
  },
32
36
  ],
33
37
  },
@@ -2,9 +2,11 @@ import { __awaiter, __decorate } from "tslib";
2
2
  import { customElement, GenesisElement, observable, volatile } from '@genesislcap/web-core';
3
3
  import { Assignment, defaultUpdateType, Severity, } from '../../../../../notify.types';
4
4
  import { NotifyService } from '../../../../../services/notify.service';
5
+ import { RuleService } from '../../../../../services/rule.service';
5
6
  import { SystemService } from '../../../../../services/system.service';
6
7
  import { TemplateService } from '../../../../../services/template.service';
7
8
  import { humanize, logger } from '../../../../../utils';
9
+ import { AlertSoundAttachmentMixin } from '../../../alert-sound-attachment';
8
10
  import { DynamicRuleUtils, showNotificationError, TemplateUtils, isEmpty, } from '../../../notification-dashboard.utils';
9
11
  import { DynamicRuleDialogStyles } from '../../../styles/dynamic-rule.styles';
10
12
  import { TemplateDialogTemplate } from './template-dialog.template';
@@ -15,7 +17,7 @@ import { TemplateDialogMode } from './template-dialog.types';
15
17
  *
16
18
  * @fires close - Fired when the dialog should close
17
19
  */
18
- let TemplateDialog = class TemplateDialog extends GenesisElement {
20
+ let TemplateDialog = class TemplateDialog extends AlertSoundAttachmentMixin(GenesisElement) {
19
21
  constructor() {
20
22
  super(...arguments);
21
23
  this.templateDialogMode = null;
@@ -39,6 +41,17 @@ let TemplateDialog = class TemplateDialog extends GenesisElement {
39
41
  this.topics = [];
40
42
  this.requiredFields = () => [this.name, this.description, this.header, this.message];
41
43
  }
44
+ get isAlertSoundEditMode() {
45
+ return this.templateDialogMode === TemplateDialogMode.EDIT;
46
+ }
47
+ get alertSoundEntityId() {
48
+ return this.templateId;
49
+ }
50
+ get alertSoundCacheSyncMode() {
51
+ // Template sound updates propagate to instance rules by NAME on the server;
52
+ // bust the whole client cache so those instance ids re-fetch.
53
+ return 'all';
54
+ }
42
55
  openDialog(params) {
43
56
  return __awaiter(this, void 0, void 0, function* () {
44
57
  this.topics = yield this.notifyService.getNotifyRouteTopics();
@@ -59,6 +72,7 @@ let TemplateDialog = class TemplateDialog extends GenesisElement {
59
72
  });
60
73
  }
61
74
  close() {
75
+ this.stopTestSound();
62
76
  // Objects
63
77
  this.templateId = null;
64
78
  this.name = null;
@@ -69,6 +83,7 @@ let TemplateDialog = class TemplateDialog extends GenesisElement {
69
83
  this.topic = null;
70
84
  this.severity = null;
71
85
  this.templateDialogMode = null;
86
+ this.resetAlertSoundState();
72
87
  // Arrays
73
88
  this.resources = [];
74
89
  this.topics = [];
@@ -113,6 +128,7 @@ let TemplateDialog = class TemplateDialog extends GenesisElement {
113
128
  this.conditions = DynamicRuleUtils.resolveLeftValues(loadedConditions, this.fields);
114
129
  this.refreshExpressionPreview();
115
130
  this.attributeAssignments((_c = template.RESULT_EXPRESSION) === null || _c === void 0 ? void 0 : _c.ASSIGNMENTS);
131
+ yield this.loadExistingAlertSound(template.DYNAMIC_RULE_ID);
116
132
  });
117
133
  }
118
134
  refreshExpressionPreview() {
@@ -168,7 +184,7 @@ let TemplateDialog = class TemplateDialog extends GenesisElement {
168
184
  DynamicRuleUtils.hasOrJoin(this.conditions) ||
169
185
  DynamicRuleUtils.hasGroup(this.conditions) ||
170
186
  DynamicRuleUtils.hasUnsupportedOperator(this.conditions);
171
- return Object.assign(Object.assign({}, (this.templateDialogMode === TemplateDialogMode.EDIT && {
187
+ const template = Object.assign(Object.assign({}, (this.templateDialogMode === TemplateDialogMode.EDIT && {
172
188
  DYNAMIC_RULE_ID: this.templateId,
173
189
  })), { RULE_NAME: this.name, RULE_DESCRIPTION: this.description, RULE_TABLE: this.resource, TABLE_OPERATIONS: this.updateType, RULE_EXPRESSION: useRaw
174
190
  ? null
@@ -182,9 +198,11 @@ let TemplateDialog = class TemplateDialog extends GenesisElement {
182
198
  DynamicRuleUtils.createAssignment(Assignment.NOTIFY_SEVERITY, humanize(this.severity)),
183
199
  ],
184
200
  }, PARAMETER_DETAILS: useRaw ? {} : TemplateUtils.createParameterDetails(this.parameters) });
201
+ return this.applyAlertSoundToPayload(template);
185
202
  }
186
203
  validateResponse(response) {
187
204
  if (response.MESSAGE_TYPE === 'EVENT_ACK') {
205
+ this.syncAlertSoundCacheAfterSave();
188
206
  this.close();
189
207
  return;
190
208
  }
@@ -245,6 +263,9 @@ __decorate([
245
263
  __decorate([
246
264
  NotifyService
247
265
  ], TemplateDialog.prototype, "notifyService", void 0);
266
+ __decorate([
267
+ RuleService
268
+ ], TemplateDialog.prototype, "ruleService", void 0);
248
269
  __decorate([
249
270
  observable
250
271
  ], TemplateDialog.prototype, "templateDialogMode", void 0);
@@ -2,6 +2,7 @@ import { sync } from '@genesislcap/foundation-utils';
2
2
  import { html, ref, repeat, when } from '@genesislcap/web-core';
3
3
  import { Severity, UpdateType } from '../../../../../notify.types';
4
4
  import { eventDetail } from '../../../../../utils/eventDetail';
5
+ import { alertSoundAttachmentTemplate } from '../../../alert-sound-attachment';
5
6
  const isGroup = (node) => Array.isArray(node.CHILDREN);
6
7
  export const TemplateDialogTemplate = html `
7
8
  <rapid-modal ${ref('dialog')} class="dialog" :onCloseCallback=${(x) => () => x.close()}>
@@ -97,6 +98,8 @@ export const TemplateDialogTemplate = html `
97
98
  </div>
98
99
  </div>
99
100
 
101
+ ${alertSoundAttachmentTemplate}
102
+
100
103
  <div class="control">
101
104
  <label>Condition(s)</label>
102
105
  <div class="items">
@@ -2,6 +2,7 @@ import { __awaiter, __decorate } from "tslib";
2
2
  import { showNotificationDialog } from '@genesislcap/foundation-notifications';
3
3
  import { customElement, GenesisElement, html, ref, repeat } from '@genesislcap/web-core';
4
4
  import { TemplateService } from '../../../../services/template.service';
5
+ import { AlertSoundPlayer } from '../../../../utils/alert-sound';
5
6
  import { DynamicRuleManagementStyles } from '../../styles/dynamic-rule.styles';
6
7
  import { TEMPLATE_MANAGEMENT_COLUMNS } from './columns';
7
8
  import { TemplateDialogMode } from './template-dialog/template-dialog.types';
@@ -27,7 +28,13 @@ let TemplateManagement = class TemplateManagement extends GenesisElement {
27
28
  confirmingActions: [
28
29
  {
29
30
  label: 'Confirm',
30
- action: () => this.templateService.deleteRuleTemplate(template.ID),
31
+ action: () => {
32
+ // Template sounds propagate to instance rules by NAME on the
33
+ // server; the client cache is keyed by those instance ids, not
34
+ // template.ID — bust everything so deleted audio stops playing.
35
+ AlertSoundPlayer.shared.clearAllServerSounds();
36
+ this.templateService.deleteRuleTemplate(template.ID);
37
+ },
31
38
  },
32
39
  ],
33
40
  },
@@ -90,7 +90,7 @@ export const DynamicRuleDialogStyles = css `
90
90
  display: flex;
91
91
  flex-direction: column;
92
92
  width: ${String(modalWidthPx)}px;
93
- max-height: 500px;
93
+ max-height: 600px;
94
94
  overflow-y: scroll;
95
95
  gap: calc(var(--design-unit) * 3px);
96
96
  padding: calc(var(--design-unit) * 4px);
@@ -161,5 +161,35 @@ export const DynamicRuleDialogStyles = css `
161
161
  min-height: calc(var(--design-unit) * 12px);
162
162
  resize: vertical;
163
163
  }
164
+
165
+ .alert-sound {
166
+ display: flex;
167
+ flex-direction: column;
168
+ gap: calc(var(--design-unit) * 2px);
169
+ margin-top: calc(var(--design-unit) * 2px);
170
+ }
171
+
172
+ .alert-sound .file-input {
173
+ display: none;
174
+ }
175
+
176
+ .alert-sound-row {
177
+ display: flex;
178
+ align-items: center;
179
+ gap: calc(var(--design-unit) * 2px);
180
+ flex-wrap: wrap;
181
+ border: calc(var(--stroke-width) * 1px) solid var(--neutral-stroke-divider-rest);
182
+ border-radius: calc(var(--control-corner-radius) * 1px);
183
+ padding: calc(var(--design-unit) * 2px);
184
+ }
185
+
186
+ .alert-sound-name {
187
+ flex: 1;
188
+ min-width: 120px;
189
+ color: var(--neutral-foreground-hint);
190
+ overflow: hidden;
191
+ text-overflow: ellipsis;
192
+ white-space: nowrap;
193
+ }
164
194
  `;
165
195
  // #endregion
@@ -0,0 +1,283 @@
1
+ import { __awaiter, __decorate } from "tslib";
2
+ import { customElement, GenesisElement, observable } from '@genesislcap/web-core';
3
+ import { RuleService } from '../../services/rule.service';
4
+ import { AlertSoundPlayer, MAX_SOUND_RULES, } from '../../utils/alert-sound';
5
+ import { FoundationSoundsPanelStyles } from './foundation-sounds-panel.styles';
6
+ import { FoundationSoundsPanelTemplate } from './foundation-sounds-panel.template';
7
+ /**
8
+ * Configures alert sound rules in the inbox Sounds tab. Persists via the shared
9
+ * AlertSoundPlayer; playback is handled by layout-mounted
10
+ * {@link FoundationSounds}.
11
+ *
12
+ * @public
13
+ */
14
+ let FoundationSoundsPanel = class FoundationSoundsPanel extends GenesisElement {
15
+ constructor() {
16
+ super(...arguments);
17
+ /** @internal */
18
+ this.rules = [];
19
+ /** @internal */
20
+ this.canAddRule = true;
21
+ /** @internal */
22
+ this.canRemoveRule = false;
23
+ /** Global playback volume 0–100. @internal */
24
+ this.volumePercent = 100;
25
+ /** @internal */
26
+ this.muted = false;
27
+ /** Notification rules from REQ_NOTIFICATION_RULE. @internal */
28
+ this.selectableRules = [];
29
+ /** @internal */
30
+ this.ruleSelectOptions = [
31
+ { id: '', name: 'Select a rule...' },
32
+ ];
33
+ /** @internal */
34
+ this.maxRules = MAX_SOUND_RULES;
35
+ this.pendingRuleIndex = null;
36
+ this.player = AlertSoundPlayer.shared;
37
+ /** Suppresses select change events while options are rebuilt. */
38
+ this.ignoreSelectChanges = false;
39
+ }
40
+ connectedCallback() {
41
+ const _super = Object.create(null, {
42
+ connectedCallback: { get: () => super.connectedCallback }
43
+ });
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ _super.connectedCallback.call(this);
46
+ yield this.player.restore();
47
+ // The await above can outlive this element (e.g. the inbox flyout closing
48
+ // before restore() settles). Without this guard, disconnectedCallback runs
49
+ // first with unsubscribePlayer still unset, so it clears nothing — and we
50
+ // then add a listener to the shared AlertSoundPlayer.shared that nothing
51
+ // ever removes, plus fire a redundant loadRules() request.
52
+ if (!this.isConnected) {
53
+ return;
54
+ }
55
+ this.syncSoundState();
56
+ this.unsubscribePlayer = this.player.subscribe(() => this.syncSoundState());
57
+ yield this.loadRules();
58
+ });
59
+ }
60
+ disconnectedCallback() {
61
+ var _a;
62
+ super.disconnectedCallback();
63
+ (_a = this.unsubscribePlayer) === null || _a === void 0 ? void 0 : _a.call(this);
64
+ this.unsubscribePlayer = undefined;
65
+ }
66
+ onMatchModeChange(index, event) {
67
+ const value = event.target.value;
68
+ const matchMode = value === 'rule' ? 'rule' : value === 'header' ? 'header' : 'message';
69
+ if (matchMode === 'rule') {
70
+ void this.loadRules();
71
+ }
72
+ this.player.setMatchMode(index, matchMode);
73
+ // FAST preventDefaults when a binding returns void — always allow the event.
74
+ return true;
75
+ }
76
+ onPatternInput(index, event) {
77
+ const value = event.target.value;
78
+ this.player.setPattern(index, value);
79
+ return true;
80
+ }
81
+ onRuleChange(index, event) {
82
+ if (this.ignoreSelectChanges) {
83
+ return true;
84
+ }
85
+ const ruleId = event.target.value;
86
+ if (!ruleId) {
87
+ this.player.setRule(index, null);
88
+ return true;
89
+ }
90
+ const rule = this.selectableRules.find((item) => item.id === ruleId);
91
+ if (!rule) {
92
+ return true;
93
+ }
94
+ this.player.setRule(index, rule);
95
+ return true;
96
+ }
97
+ openFilePicker(index) {
98
+ var _a;
99
+ this.pendingRuleIndex = index;
100
+ (_a = this.fileInput) === null || _a === void 0 ? void 0 : _a.click();
101
+ return true;
102
+ }
103
+ onFileSelected(event) {
104
+ var _a;
105
+ const input = event.target;
106
+ const file = (_a = input.files) === null || _a === void 0 ? void 0 : _a[0];
107
+ const index = this.pendingRuleIndex;
108
+ this.pendingRuleIndex = null;
109
+ input.value = '';
110
+ if (!file || index == null) {
111
+ return true;
112
+ }
113
+ this.player.setSoundFromFile(index, file);
114
+ return true;
115
+ }
116
+ clearSound(index) {
117
+ this.player.clearSound(index);
118
+ return true;
119
+ }
120
+ testSound(index) {
121
+ this.player.playRule(index);
122
+ return true;
123
+ }
124
+ toggleRuleMute(index) {
125
+ this.player.toggleRuleMute(index);
126
+ return true;
127
+ }
128
+ onVolumeChange(event) {
129
+ const value = Number(event.target.value);
130
+ if (!Number.isFinite(value)) {
131
+ return true;
132
+ }
133
+ this.player.setVolumePercent(value);
134
+ return true;
135
+ }
136
+ toggleMute() {
137
+ this.player.toggleMute();
138
+ return true;
139
+ }
140
+ addRule() {
141
+ this.player.addRule();
142
+ return true;
143
+ }
144
+ removeRule(index) {
145
+ this.player.removeRule(index);
146
+ return true;
147
+ }
148
+ /** Reloads the notification-rule picker (e.g. when the Sounds tab is opened). */
149
+ reloadRules() {
150
+ return __awaiter(this, void 0, void 0, function* () {
151
+ yield this.loadRules();
152
+ });
153
+ }
154
+ loadRules() {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ try {
157
+ const rules = yield this.ruleService.getRules();
158
+ const ruleList = (Array.isArray(rules) ? rules : []);
159
+ this.selectableRules = this.mapRules(ruleList);
160
+ this.ruleSelectOptions = [
161
+ {
162
+ id: '',
163
+ name: this.selectableRules.length ? 'Select a rule...' : 'No rules available',
164
+ },
165
+ ...this.selectableRules.map((rule) => ({ id: rule.id, name: rule.name })),
166
+ ];
167
+ yield this.settleRuleOptions();
168
+ this.syncSoundState();
169
+ // options-datasource clearData() wipes select.value whenever :data is
170
+ // reassigned, and :value won't re-apply if templateId didn't change — so
171
+ // push persisted rule ids back onto the selects after the rebuild.
172
+ this.applyRuleSelectValues();
173
+ }
174
+ catch (_a) {
175
+ this.selectableRules = [];
176
+ this.ruleSelectOptions = [{ id: '', name: 'No rules available' }];
177
+ // Without this, a failed REQ_NOTIFICATION_RULE leaves a restored
178
+ // matchMode: 'rule' row showing the "Select a rule..." placeholder for
179
+ // good, even though the player still holds its templateId and plays fine.
180
+ this.syncSoundState();
181
+ this.applyRuleSelectValues();
182
+ }
183
+ });
184
+ }
185
+ /**
186
+ * Suppresses select `change` events only while options-datasource rebuilds
187
+ * the DOM — not for the `getRules()` round trip itself, so a user picking a
188
+ * rule while a request is in flight isn't silently dropped.
189
+ */
190
+ settleRuleOptions() {
191
+ return __awaiter(this, void 0, void 0, function* () {
192
+ this.ignoreSelectChanges = true;
193
+ try {
194
+ yield new Promise((resolve) => {
195
+ requestAnimationFrame(() => {
196
+ resolve();
197
+ });
198
+ });
199
+ }
200
+ finally {
201
+ this.ignoreSelectChanges = false;
202
+ }
203
+ });
204
+ }
205
+ /**
206
+ * Re-applies persisted template ids onto `.rule-select` hosts after
207
+ * options-datasource has rebuilt its options. We deliberately do *not* bind
208
+ * `:initialSelectedValue` to templateId: that path compares the option label
209
+ * against Select.value (the option id), always mismatches, clearData()'s the
210
+ * select, and leaves the next pick stuck on "Select a rule...".
211
+ */
212
+ applyRuleSelectValues() {
213
+ var _a;
214
+ (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.sound-rule').forEach((row) => {
215
+ var _a;
216
+ const index = Number(row.getAttribute('data-rule-index'));
217
+ const rule = this.rules.find((item) => item.index === index);
218
+ const select = row.querySelector('.rule-select');
219
+ if (!select || !rule || rule.matchMode !== 'rule') {
220
+ return;
221
+ }
222
+ select.value = (_a = rule.templateId) !== null && _a !== void 0 ? _a : '';
223
+ });
224
+ }
225
+ mapRules(ruleList) {
226
+ const seenIds = new Set();
227
+ return ruleList
228
+ .map((rule) => {
229
+ var _a, _b, _c;
230
+ const name = (_c = (_b = (_a = rule === null || rule === void 0 ? void 0 : rule.NAME) !== null && _a !== void 0 ? _a : rule === null || rule === void 0 ? void 0 : rule.RULE_NAME) !== null && _b !== void 0 ? _b : rule === null || rule === void 0 ? void 0 : rule.name) !== null && _c !== void 0 ? _c : '';
231
+ const id = this.ruleMatchId(rule);
232
+ if (!id || !name || seenIds.has(id)) {
233
+ return null;
234
+ }
235
+ seenIds.add(id);
236
+ return { id, name };
237
+ })
238
+ .filter((rule) => Boolean(rule));
239
+ }
240
+ ruleMatchId(rule) {
241
+ var _a, _b, _c;
242
+ return String((_c = (_b = (_a = rule === null || rule === void 0 ? void 0 : rule.DYNAMIC_RULE_ID) !== null && _a !== void 0 ? _a : rule === null || rule === void 0 ? void 0 : rule.ID) !== null && _b !== void 0 ? _b : rule === null || rule === void 0 ? void 0 : rule.id) !== null && _c !== void 0 ? _c : '').trim();
243
+ }
244
+ syncSoundState() {
245
+ this.rules = this.player.rules;
246
+ this.canAddRule = this.player.canAddRule;
247
+ this.canRemoveRule = this.player.canRemoveRule;
248
+ this.volumePercent = this.player.volumePercent;
249
+ this.muted = this.player.isMuted;
250
+ }
251
+ };
252
+ __decorate([
253
+ RuleService
254
+ ], FoundationSoundsPanel.prototype, "ruleService", void 0);
255
+ __decorate([
256
+ observable
257
+ ], FoundationSoundsPanel.prototype, "rules", void 0);
258
+ __decorate([
259
+ observable
260
+ ], FoundationSoundsPanel.prototype, "canAddRule", void 0);
261
+ __decorate([
262
+ observable
263
+ ], FoundationSoundsPanel.prototype, "canRemoveRule", void 0);
264
+ __decorate([
265
+ observable
266
+ ], FoundationSoundsPanel.prototype, "volumePercent", void 0);
267
+ __decorate([
268
+ observable
269
+ ], FoundationSoundsPanel.prototype, "muted", void 0);
270
+ __decorate([
271
+ observable
272
+ ], FoundationSoundsPanel.prototype, "selectableRules", void 0);
273
+ __decorate([
274
+ observable
275
+ ], FoundationSoundsPanel.prototype, "ruleSelectOptions", void 0);
276
+ FoundationSoundsPanel = __decorate([
277
+ customElement({
278
+ name: 'foundation-sounds-panel',
279
+ template: FoundationSoundsPanelTemplate,
280
+ styles: FoundationSoundsPanelStyles,
281
+ })
282
+ ], FoundationSoundsPanel);
283
+ export { FoundationSoundsPanel };