@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.
- package/README.md +39 -0
- package/custom-elements-manifest.config.js +5 -0
- package/dist/custom-elements.json +1771 -321
- package/dist/dts/components/components.d.ts.map +1 -1
- package/dist/dts/components/foundation-inbox/inbox-base/inbox-base.d.ts +2 -0
- package/dist/dts/components/foundation-inbox/inbox-base/inbox-base.d.ts.map +1 -1
- package/dist/dts/components/foundation-inbox/inbox.styles.d.ts.map +1 -1
- package/dist/dts/components/foundation-inbox/inbox.template.d.ts.map +1 -1
- package/dist/dts/components/foundation-inbox/inbox.types.d.ts +4 -1
- package/dist/dts/components/foundation-inbox/inbox.types.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/alert-sound-attachment.d.ts +86 -0
- package/dist/dts/components/foundation-notification-dashboard/alert-sound-attachment.d.ts.map +1 -0
- package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.d.ts +39 -1
- package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.template.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types.d.ts +8 -0
- package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/components/rules/rule-management.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.d.ts +41 -1
- package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.template.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.types.d.ts +2 -1
- package/dist/dts/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.types.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/components/templates/template-management.d.ts.map +1 -1
- package/dist/dts/components/foundation-notification-dashboard/styles/dynamic-rule.styles.d.ts.map +1 -1
- package/dist/dts/components/foundation-sounds/foundation-sounds-panel.d.ts +73 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds-panel.d.ts.map +1 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds-panel.styles.d.ts +2 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds-panel.styles.d.ts.map +1 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds-panel.template.d.ts +3 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds-panel.template.d.ts.map +1 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds.d.ts +34 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds.d.ts.map +1 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds.styles.d.ts +3 -0
- package/dist/dts/components/foundation-sounds/foundation-sounds.styles.d.ts.map +1 -0
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/react.d.ts +31 -11
- package/dist/dts/services/inbox.service.d.ts +6 -0
- package/dist/dts/services/inbox.service.d.ts.map +1 -1
- package/dist/dts/services/rule.service.d.ts +2 -1
- package/dist/dts/services/rule.service.d.ts.map +1 -1
- package/dist/dts/utils/alert-sound.d.ts +177 -0
- package/dist/dts/utils/alert-sound.d.ts.map +1 -0
- package/dist/dts/utils/alert-sound.test.d.ts +2 -0
- package/dist/dts/utils/alert-sound.test.d.ts.map +1 -0
- package/dist/esm/components/components.js +4 -0
- package/dist/esm/components/foundation-inbox/inbox-base/inbox-base.js +6 -0
- package/dist/esm/components/foundation-inbox/inbox.styles.js +15 -0
- package/dist/esm/components/foundation-inbox/inbox.template.js +16 -0
- package/dist/esm/components/foundation-inbox/inbox.types.js +1 -0
- package/dist/esm/components/foundation-notification-dashboard/alert-sound-attachment.js +218 -0
- package/dist/esm/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.js +17 -2
- package/dist/esm/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.template.js +3 -0
- package/dist/esm/components/foundation-notification-dashboard/components/rules/rule-management.js +5 -1
- package/dist/esm/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.js +23 -2
- package/dist/esm/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.template.js +3 -0
- package/dist/esm/components/foundation-notification-dashboard/components/templates/template-management.js +8 -1
- package/dist/esm/components/foundation-notification-dashboard/styles/dynamic-rule.styles.js +31 -1
- package/dist/esm/components/foundation-sounds/foundation-sounds-panel.js +283 -0
- package/dist/esm/components/foundation-sounds/foundation-sounds-panel.styles.js +200 -0
- package/dist/esm/components/foundation-sounds/foundation-sounds-panel.template.js +179 -0
- package/dist/esm/components/foundation-sounds/foundation-sounds.js +206 -0
- package/dist/esm/components/foundation-sounds/foundation-sounds.styles.js +7 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/services/inbox.service.js +6 -2
- package/dist/esm/services/rule.service.js +35 -0
- package/dist/esm/utils/alert-sound.js +955 -0
- package/dist/esm/utils/alert-sound.test.js +25 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/pbc-notify-ui.api.json +2859 -332
- package/dist/pbc-notify-ui.d.ts +281 -3
- package/dist/react.cjs +19 -5
- package/dist/react.mjs +16 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/components/components.ts +4 -0
- package/src/components/foundation-inbox/inbox-base/inbox-base.ts +7 -0
- package/src/components/foundation-inbox/inbox.styles.ts +15 -0
- package/src/components/foundation-inbox/inbox.template.ts +16 -0
- package/src/components/foundation-inbox/inbox.types.ts +3 -0
- package/src/components/foundation-notification-dashboard/alert-sound-attachment.ts +283 -0
- package/src/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.template.ts +3 -0
- package/src/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.ts +21 -2
- package/src/components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types.ts +9 -0
- package/src/components/foundation-notification-dashboard/components/rules/rule-management.ts +5 -1
- package/src/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.template.ts +3 -0
- package/src/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.ts +25 -2
- package/src/components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog.types.ts +2 -1
- package/src/components/foundation-notification-dashboard/components/templates/template-management.ts +8 -1
- package/src/components/foundation-notification-dashboard/styles/dynamic-rule.styles.ts +31 -1
- package/src/components/foundation-sounds/foundation-sounds-panel.styles.ts +201 -0
- package/src/components/foundation-sounds/foundation-sounds-panel.template.ts +188 -0
- package/src/components/foundation-sounds/foundation-sounds-panel.ts +280 -0
- package/src/components/foundation-sounds/foundation-sounds.styles.ts +8 -0
- package/src/components/foundation-sounds/foundation-sounds.ts +222 -0
- package/src/index.ts +8 -0
- package/src/services/inbox.service.ts +12 -2
- package/src/services/rule.service.ts +37 -1
- package/src/utils/alert-sound.test.ts +32 -0
- package/src/utils/alert-sound.ts +1135 -0
- package/src/utils/index.ts +3 -0
package/dist/dts/react.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ import type { TemplateConditionGroup as TemplateConditionGroupWC } from './compo
|
|
|
27
27
|
import type { TemplateDialog as TemplateDialogWC } from './components/foundation-notification-dashboard/components/templates/template-dialog/template-dialog';
|
|
28
28
|
import type { TemplateManagement as TemplateManagementWC } from './components/foundation-notification-dashboard/components/templates/template-management';
|
|
29
29
|
import type { FoundationNotificationDashboard as FoundationNotificationDashboardWC } from './components/foundation-notification-dashboard/notification-dashboard';
|
|
30
|
+
import type { FoundationSoundsPanel as FoundationSoundsPanelWC } from './components/foundation-sounds/foundation-sounds-panel';
|
|
31
|
+
import type { FoundationSounds as FoundationSoundsWC } from './components/foundation-sounds/foundation-sounds';
|
|
30
32
|
import type { ReconciliationSandbox as ReconciliationSandboxWC } from './sandbox';
|
|
31
33
|
import type { ConditionBuilderEntity, ParameterBuilderEntity, ParameterEditedEventDetail } from '@genesislcap/pbc-notify-ui';
|
|
32
34
|
|
|
@@ -86,6 +88,24 @@ export declare const FoundationNotificationDashboard: React.ForwardRefExoticComp
|
|
|
86
88
|
>;
|
|
87
89
|
export type FoundationNotificationDashboardRef = FoundationNotificationDashboardWC;
|
|
88
90
|
|
|
91
|
+
export declare const FoundationSoundsPanel: React.ForwardRefExoticComponent<
|
|
92
|
+
React.PropsWithChildren<
|
|
93
|
+
Omit<PublicOf<FoundationSoundsPanelWC>, 'children' | 'style'> &
|
|
94
|
+
HTMLWCProps & {
|
|
95
|
+
}
|
|
96
|
+
> & React.RefAttributes<FoundationSoundsPanelWC>
|
|
97
|
+
>;
|
|
98
|
+
export type FoundationSoundsPanelRef = FoundationSoundsPanelWC;
|
|
99
|
+
|
|
100
|
+
export declare const FoundationSounds: React.ForwardRefExoticComponent<
|
|
101
|
+
React.PropsWithChildren<
|
|
102
|
+
Omit<PublicOf<FoundationSoundsWC>, 'children' | 'style'> &
|
|
103
|
+
HTMLWCProps & {
|
|
104
|
+
}
|
|
105
|
+
> & React.RefAttributes<FoundationSoundsWC>
|
|
106
|
+
>;
|
|
107
|
+
export type FoundationSoundsRef = FoundationSoundsWC;
|
|
108
|
+
|
|
89
109
|
export declare const FoundationInboxCounter: React.ForwardRefExoticComponent<
|
|
90
110
|
React.PropsWithChildren<
|
|
91
111
|
Omit<PublicOf<FoundationInboxCounterWC>, 'children' | 'style'> &
|
|
@@ -235,17 +255,6 @@ export declare const TemplateDialog: React.ForwardRefExoticComponent<
|
|
|
235
255
|
>;
|
|
236
256
|
export type TemplateDialogRef = TemplateDialogWC;
|
|
237
257
|
|
|
238
|
-
export declare const RuleConditionGroup: React.ForwardRefExoticComponent<
|
|
239
|
-
React.PropsWithChildren<
|
|
240
|
-
Omit<PublicOf<RuleConditionGroupWC>, 'children' | 'style'> &
|
|
241
|
-
HTMLWCProps & {
|
|
242
|
-
onDelete?: (event: CustomEvent<unknown>) => void;
|
|
243
|
-
onEdit?: (event: CustomEvent<unknown>) => void;
|
|
244
|
-
}
|
|
245
|
-
> & React.RefAttributes<RuleConditionGroupWC>
|
|
246
|
-
>;
|
|
247
|
-
export type RuleConditionGroupRef = RuleConditionGroupWC;
|
|
248
|
-
|
|
249
258
|
export declare const RuleConditionBuilder: React.ForwardRefExoticComponent<
|
|
250
259
|
React.PropsWithChildren<
|
|
251
260
|
Omit<PublicOf<RuleConditionBuilderWC>, 'children' | 'style'> &
|
|
@@ -257,6 +266,17 @@ export declare const RuleConditionBuilder: React.ForwardRefExoticComponent<
|
|
|
257
266
|
>;
|
|
258
267
|
export type RuleConditionBuilderRef = RuleConditionBuilderWC;
|
|
259
268
|
|
|
269
|
+
export declare const RuleConditionGroup: React.ForwardRefExoticComponent<
|
|
270
|
+
React.PropsWithChildren<
|
|
271
|
+
Omit<PublicOf<RuleConditionGroupWC>, 'children' | 'style'> &
|
|
272
|
+
HTMLWCProps & {
|
|
273
|
+
onDelete?: (event: CustomEvent<unknown>) => void;
|
|
274
|
+
onEdit?: (event: CustomEvent<unknown>) => void;
|
|
275
|
+
}
|
|
276
|
+
> & React.RefAttributes<RuleConditionGroupWC>
|
|
277
|
+
>;
|
|
278
|
+
export type RuleConditionGroupRef = RuleConditionGroupWC;
|
|
279
|
+
|
|
260
280
|
export declare const ParameterBuilder: React.ForwardRefExoticComponent<
|
|
261
281
|
React.PropsWithChildren<
|
|
262
282
|
Omit<PublicOf<ParameterBuilderWC>, 'children' | 'style'> &
|
|
@@ -7,6 +7,12 @@ export declare const INBOX_PAGE_SIZE = 20;
|
|
|
7
7
|
export interface FoundationInboxServiceStore {
|
|
8
8
|
items: Array<any>;
|
|
9
9
|
moreRows: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* SEQUENCE_ID of the last message applied to {@link items}. `<= 1` means the
|
|
12
|
+
* message was (part of) the initial snapshot rather than a real-time update —
|
|
13
|
+
* mirrors the boundary `notification-listener` uses (`SEQUENCE_ID > 1`).
|
|
14
|
+
*/
|
|
15
|
+
sequenceId: number;
|
|
10
16
|
}
|
|
11
17
|
export type SubscribeCallback = ({ store }: {
|
|
12
18
|
store: FoundationInboxServiceStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbox.service.d.ts","sourceRoot":"","sources":["../../../src/services/inbox.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAIpG;;;GAGG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"inbox.service.d.ts","sourceRoot":"","sources":["../../../src/services/inbox.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAIpG;;;GAGG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,2BAA2B,CAAA;CAAE,KAAK,GAAG,CAAC;AAE3F,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,kBAAkB,EAAE,kBAAkB,CAAC;CACxC,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;OAQG;IACH,SAAS,CACP,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,GACd,IAAI,CAAC;IACR,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC/C,WAAW,IAAI,IAAI,CAAC;CACrB;AAuGD,eAAO,MAAM,sBAAsB,8EAElC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Message } from '@genesislcap/foundation-comms';
|
|
2
|
-
import type { Rule } from '../components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types';
|
|
2
|
+
import type { NotifyAlertSound, Rule } from '../components/foundation-notification-dashboard/components/rules/rule-dialog/rule-dialog.types';
|
|
3
3
|
export interface RuleService {
|
|
4
4
|
subscribeRule(dynamicRuleId: string, parameterDetails: any): any;
|
|
5
5
|
unsubscribeRule(dynamicRuleId: string): any;
|
|
@@ -7,6 +7,7 @@ export interface RuleService {
|
|
|
7
7
|
disableRule(dynamicRuleId: string): any;
|
|
8
8
|
getRules(): Promise<any>;
|
|
9
9
|
getRuleDetails(ruleId: string): Promise<Rule>;
|
|
10
|
+
getAlertSound(dynamicRuleId: string): Promise<NotifyAlertSound | null>;
|
|
10
11
|
createRule(rule: Rule): Promise<Message>;
|
|
11
12
|
updateRule(rule: Rule): Promise<Message>;
|
|
12
13
|
deleteRule(ruleId: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.service.d.ts","sourceRoot":"","sources":["../../../src/services/rule.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"rule.service.d.ts","sourceRoot":"","sources":["../../../src/services/rule.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,KAAK,EACV,gBAAgB,EAChB,IAAI,EACL,MAAM,gGAAgG,CAAC;AAExG,MAAM,WAAW,WAAW;IAC1B,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAE;IAC5D,eAAe,CAAC,aAAa,EAAE,MAAM,OAAE;IACvC,UAAU,CAAC,aAAa,EAAE,MAAM,OAAE;IAClC,WAAW,CAAC,aAAa,EAAE,MAAM,OAAE;IACnC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IACzB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACvE,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAkJD,eAAO,MAAM,WAAW,mEAAuE,CAAC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/** Maximum number of sound rules (MESSAGE/HEADER pattern, or notification rule). */
|
|
2
|
+
export declare const MAX_SOUND_RULES = 10;
|
|
3
|
+
/**
|
|
4
|
+
* Every rule's audio is held in one IndexedDB row, so a generous per-file limit
|
|
5
|
+
* still risks the origin storage quota once multiplied by {@link MAX_SOUND_RULES}.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MAX_SOUND_FILE_BYTES: number;
|
|
8
|
+
/** Encodes a File/Blob to a base64 string (no data-URL prefix). */
|
|
9
|
+
export declare function blobToBase64(blob: Blob): Promise<string>;
|
|
10
|
+
/** Decodes a base64 string to a Blob. */
|
|
11
|
+
export declare function base64ToBlob(base64: string, mimeType: string): Blob;
|
|
12
|
+
/** How a sound rule decides when to play. */
|
|
13
|
+
export type AlertSoundMatchMode = 'message' | 'header' | 'rule';
|
|
14
|
+
/** Notification rule option available for DYNAMIC_RULE_ID sound binding. */
|
|
15
|
+
export type AlertSoundSelectableRule = {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolves a playable MIME type. macOS often leaves `.aif` / `.aiff` with an
|
|
21
|
+
* empty `File.type`; falling back to `audio/mpeg` breaks restore + decode.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveAudioMimeType(name?: string | null, type?: string | null): string;
|
|
24
|
+
/** Formats a byte count for user-facing size limits (e.g. "2 MB"). */
|
|
25
|
+
export declare function formatBytes(bytes: number): string;
|
|
26
|
+
export type AlertSoundRuleView = {
|
|
27
|
+
index: number;
|
|
28
|
+
matchMode: AlertSoundMatchMode;
|
|
29
|
+
pattern: string;
|
|
30
|
+
templateId: string | null;
|
|
31
|
+
templateName: string | null;
|
|
32
|
+
fileName: string | null;
|
|
33
|
+
hasSound: boolean;
|
|
34
|
+
muted: boolean;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* True when `text` contains `phrase` as a whole phrase (case-insensitive).
|
|
38
|
+
* Single words and multi-word phrases both work; edges use word boundaries so
|
|
39
|
+
* e.g. `g1` matches "test g1 test" but not "test g1h test".
|
|
40
|
+
*
|
|
41
|
+
* `\b` only asserts a word/non-word transition, so anchoring an edge whose own
|
|
42
|
+
* character is punctuation can never match — `\b\$100` fails on "cost $100".
|
|
43
|
+
* Each edge is therefore anchored only when the pattern ends in a word character.
|
|
44
|
+
*
|
|
45
|
+
* @internal Exported for unit tests.
|
|
46
|
+
*/
|
|
47
|
+
export declare function textContainsWholeWord(text: string, phrase: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Plays user-selected alert sounds matched by MESSAGE/HEADER text (word or phrase)
|
|
50
|
+
* or notification rule DYNAMIC_RULE_ID. Persists up to {@link MAX_SOUND_RULES}
|
|
51
|
+
* rules in IndexedDB.
|
|
52
|
+
*/
|
|
53
|
+
export declare class AlertSoundPlayer {
|
|
54
|
+
private slots;
|
|
55
|
+
private activeCount;
|
|
56
|
+
/** Linear gain 0–1 applied to every HTMLAudioElement. */
|
|
57
|
+
private volume;
|
|
58
|
+
private muted;
|
|
59
|
+
private listeners;
|
|
60
|
+
/** Coalesces concurrent restores from layout + Sounds tab mounts. */
|
|
61
|
+
private restorePromise;
|
|
62
|
+
private restored;
|
|
63
|
+
private persistChain;
|
|
64
|
+
/** In-memory cache of rule-attached sounds fetched from NOTIFY_ALERT_SOUND. */
|
|
65
|
+
private readonly serverSounds;
|
|
66
|
+
/** Shared player so the layout listener and inbox Sounds tab stay in sync. */
|
|
67
|
+
static readonly shared: AlertSoundPlayer;
|
|
68
|
+
get rules(): AlertSoundRuleView[];
|
|
69
|
+
get canAddRule(): boolean;
|
|
70
|
+
get canRemoveRule(): boolean;
|
|
71
|
+
/** Whole-number percent for the Sounds tab slider (0–100). */
|
|
72
|
+
get volumePercent(): number;
|
|
73
|
+
get isMuted(): boolean;
|
|
74
|
+
get isReady(): boolean;
|
|
75
|
+
subscribe(listener: () => void): () => void;
|
|
76
|
+
/**
|
|
77
|
+
* Loads rules from IndexedDB once per page session. Concurrent calls (layout
|
|
78
|
+
* listener + Sounds tab) share one promise so a late restore cannot wipe
|
|
79
|
+
* in-memory edits the user already made.
|
|
80
|
+
*/
|
|
81
|
+
restore(): Promise<boolean>;
|
|
82
|
+
private restoreFromDb;
|
|
83
|
+
/** Sets global playback volume from a 0–100 percent (Sounds tab slider). */
|
|
84
|
+
setVolumePercent(percent: number): void;
|
|
85
|
+
setMuted(muted: boolean): void;
|
|
86
|
+
toggleMute(): void;
|
|
87
|
+
addRule(): boolean;
|
|
88
|
+
removeRule(index: number): void;
|
|
89
|
+
/** Clears match criteria and sound file for a rule without removing the row. */
|
|
90
|
+
resetRule(index: number): void;
|
|
91
|
+
setMatchMode(index: number, matchMode: AlertSoundMatchMode): void;
|
|
92
|
+
/**
|
|
93
|
+
* Deliberately does not notify: the value came from the pattern field itself,
|
|
94
|
+
* so re-rendering the rule rows mid-keystroke would only disturb the caret.
|
|
95
|
+
*/
|
|
96
|
+
setPattern(index: number, pattern: string): void;
|
|
97
|
+
setRule(index: number, rule: Pick<AlertSoundSelectableRule, 'id' | 'name'> | null): void;
|
|
98
|
+
setSoundFromFile(index: number, file: File): void;
|
|
99
|
+
clearSound(index: number): void;
|
|
100
|
+
setRuleMuted(index: number, muted: boolean): void;
|
|
101
|
+
toggleRuleMute(index: number): void;
|
|
102
|
+
/** Plays the sound file for a specific rule (for UI testing). */
|
|
103
|
+
playRule(index: number): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Plays the first configured rule that matches the alert:
|
|
106
|
+
* - message: MESSAGE contains the pattern as a whole phrase (case-insensitive)
|
|
107
|
+
* - header: HEADER contains the pattern as a whole phrase (case-insensitive)
|
|
108
|
+
* - rule: alert DYNAMIC_RULE_ID equals the selected id
|
|
109
|
+
*/
|
|
110
|
+
playForAlert(alert: {
|
|
111
|
+
message?: string | null;
|
|
112
|
+
header?: string | null;
|
|
113
|
+
dynamicRuleId?: string | null;
|
|
114
|
+
}): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Caches a server-backed rule sound (from NOTIFY_ALERT_SOUND) for playback.
|
|
117
|
+
* Replaces any previous cache entry for the same dynamicRuleId.
|
|
118
|
+
*/
|
|
119
|
+
setServerSound(dynamicRuleId: string, options: {
|
|
120
|
+
blob: Blob;
|
|
121
|
+
fileName?: string;
|
|
122
|
+
mimeType?: string;
|
|
123
|
+
}): void;
|
|
124
|
+
/** True when a server sound for this rule is already cached. */
|
|
125
|
+
hasServerSound(dynamicRuleId: string): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Drops a cached server sound so the next alert re-fetches from
|
|
128
|
+
* NOTIFY_ALERT_SOUND (e.g. after replace/clear on the rule dialog).
|
|
129
|
+
*/
|
|
130
|
+
clearServerSound(dynamicRuleId: string): void;
|
|
131
|
+
/**
|
|
132
|
+
* Clears every cached server-backed rule sound. Used when a template sound
|
|
133
|
+
* changes — subscribed instance ids are unknown on the client, and alerts
|
|
134
|
+
* look up by those instance ids.
|
|
135
|
+
*/
|
|
136
|
+
clearAllServerSounds(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Plays a cached server sound for the given DYNAMIC_RULE_ID.
|
|
139
|
+
* Respects global mute / volume. Returns false if missing or muted with no play.
|
|
140
|
+
*/
|
|
141
|
+
playServerSound(dynamicRuleId: string): boolean;
|
|
142
|
+
private revokeServerSound;
|
|
143
|
+
private applyStoredMatch;
|
|
144
|
+
private matchFields;
|
|
145
|
+
/** Debounces config writes so holding down a key does not thrash IndexedDB. */
|
|
146
|
+
private readonly debouncedPersistConfig;
|
|
147
|
+
/** Schedules a debounced config write (pattern typing). */
|
|
148
|
+
private schedulePersist;
|
|
149
|
+
/** Writes config now, including anything a pending debounce had not yet flushed. */
|
|
150
|
+
private flushPersist;
|
|
151
|
+
private flushSettings;
|
|
152
|
+
/** Flushes config plus this rule's audio (sound file added/cleared/reset). */
|
|
153
|
+
private flushAudioPersist;
|
|
154
|
+
/** Chained so overlapping writes cannot land out of order. */
|
|
155
|
+
private queuePersist;
|
|
156
|
+
private applyStoredSettings;
|
|
157
|
+
private applyVolumeToSlots;
|
|
158
|
+
private applyVolumeToAudio;
|
|
159
|
+
private persistSettings;
|
|
160
|
+
/** Match config only — cheap enough to rewrite on every keystroke. */
|
|
161
|
+
private persistConfig;
|
|
162
|
+
/** One rule's audio, stored separately so a pattern edit never touches it. */
|
|
163
|
+
private persistAudio;
|
|
164
|
+
private applyFile;
|
|
165
|
+
private isValidIndex;
|
|
166
|
+
private notify;
|
|
167
|
+
private revokeObjectUrl;
|
|
168
|
+
}
|
|
169
|
+
/** Reads ALERT_ID from a streamed alert row. */
|
|
170
|
+
export declare function getAlertId(row: Record<string, unknown> | null | undefined): string | undefined;
|
|
171
|
+
/** Reads MESSAGE from a streamed alert row. */
|
|
172
|
+
export declare function getAlertMessage(row: Record<string, unknown> | null | undefined): string | undefined;
|
|
173
|
+
/** Reads HEADER from a streamed alert row. */
|
|
174
|
+
export declare function getAlertHeader(row: Record<string, unknown> | null | undefined): string | undefined;
|
|
175
|
+
/** Reads DYNAMIC_RULE_ID from a streamed alert row. */
|
|
176
|
+
export declare function getAlertDynamicRuleId(row: Record<string, unknown> | null | undefined): string | undefined;
|
|
177
|
+
//# sourceMappingURL=alert-sound.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert-sound.d.ts","sourceRoot":"","sources":["../../../src/utils/alert-sound.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert-sound.test.d.ts","sourceRoot":"","sources":["../../../src/utils/alert-sound.test.ts"],"names":[],"mappings":""}
|
|
@@ -22,11 +22,15 @@ import { TemplateConditionBuilder } from './foundation-notification-dashboard/co
|
|
|
22
22
|
import { TemplateConditionGroup } from './foundation-notification-dashboard/components/templates/template-dialog/template-condition-group/template-condition-group';
|
|
23
23
|
import { TemplateDialog } from './foundation-notification-dashboard/components/templates/template-dialog/template-dialog';
|
|
24
24
|
import { TemplateManagement } from './foundation-notification-dashboard/components/templates/template-management';
|
|
25
|
+
import { FoundationSounds } from './foundation-sounds/foundation-sounds';
|
|
26
|
+
import { FoundationSoundsPanel } from './foundation-sounds/foundation-sounds-panel';
|
|
25
27
|
// Components
|
|
26
28
|
// #region Inbox
|
|
27
29
|
FoundationInboxCounter;
|
|
28
30
|
InboxSubscription;
|
|
29
31
|
RuleParameter;
|
|
32
|
+
FoundationSounds;
|
|
33
|
+
FoundationSoundsPanel;
|
|
30
34
|
// #endregion Inbox
|
|
31
35
|
// #region Notification Dashboard
|
|
32
36
|
RuleManagement;
|
|
@@ -129,6 +129,7 @@ export class FoundationInboxBase extends GenesisElement {
|
|
|
129
129
|
// #region Tabs
|
|
130
130
|
inboxTabChanged(inboxTab) {
|
|
131
131
|
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
var _a;
|
|
132
133
|
this.selectedTab = inboxTab;
|
|
133
134
|
this.ruleTemplateDetails = null;
|
|
134
135
|
switch (inboxTab) {
|
|
@@ -143,6 +144,11 @@ export class FoundationInboxBase extends GenesisElement {
|
|
|
143
144
|
case InboxTab.SUBSCRIBE:
|
|
144
145
|
yield this.refreshSubscribeData();
|
|
145
146
|
break;
|
|
147
|
+
case InboxTab.SOUNDS: {
|
|
148
|
+
// Refresh rules so newly subscribed templates (which create rules) appear.
|
|
149
|
+
yield ((_a = this.sounds) === null || _a === void 0 ? void 0 : _a.reloadRules());
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
146
152
|
default:
|
|
147
153
|
logger.error('Unexpected inboxTab:', inboxTab);
|
|
148
154
|
break;
|
|
@@ -287,6 +287,21 @@ export const subscribe = css `
|
|
|
287
287
|
flex: 1;
|
|
288
288
|
overflow: auto;
|
|
289
289
|
}
|
|
290
|
+
|
|
291
|
+
.sounds-tab-panel {
|
|
292
|
+
display: flex;
|
|
293
|
+
flex-direction: column;
|
|
294
|
+
min-height: 0;
|
|
295
|
+
flex: 1;
|
|
296
|
+
overflow: hidden;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.sounds-tab-panel foundation-sounds-panel {
|
|
300
|
+
display: flex;
|
|
301
|
+
flex: 1;
|
|
302
|
+
min-height: 0;
|
|
303
|
+
height: 100%;
|
|
304
|
+
}
|
|
290
305
|
`;
|
|
291
306
|
export const ruleAndTemplate = css `
|
|
292
307
|
.template {
|
|
@@ -231,6 +231,14 @@ export const FoundationInboxTemplate = html `
|
|
|
231
231
|
Subscribe
|
|
232
232
|
</rapid-tab>
|
|
233
233
|
`)}
|
|
234
|
+
<rapid-tab
|
|
235
|
+
slot="sounds-tab"
|
|
236
|
+
id=${InboxTab.SOUNDS}
|
|
237
|
+
@click=${(x) => x.inboxTabChanged(InboxTab.SOUNDS)}
|
|
238
|
+
appearance="secondary"
|
|
239
|
+
>
|
|
240
|
+
Sounds
|
|
241
|
+
</rapid-tab>
|
|
234
242
|
|
|
235
243
|
<!-- Alerts -->
|
|
236
244
|
${(_) => alertsTab(InboxTab.ALERTS_NEW)} ${(_) => alertsTab(InboxTab.ALERT_HISTORY)}
|
|
@@ -318,6 +326,14 @@ export const FoundationInboxTemplate = html `
|
|
|
318
326
|
</div>
|
|
319
327
|
</rapid-tab-panel>
|
|
320
328
|
`)}
|
|
329
|
+
<rapid-tab-panel
|
|
330
|
+
slot="sounds-tab"
|
|
331
|
+
class="sounds-tab-panel"
|
|
332
|
+
id=${InboxTab.SOUNDS}
|
|
333
|
+
style=${(x) => (x.selectedTab === InboxTab.SOUNDS ? '' : 'display: none;')}
|
|
334
|
+
>
|
|
335
|
+
<foundation-sounds-panel ${ref('sounds')}></foundation-sounds-panel>
|
|
336
|
+
</rapid-tab-panel>
|
|
321
337
|
</rapid-tabs>
|
|
322
338
|
</div>
|
|
323
339
|
`;
|
|
@@ -13,4 +13,5 @@ export var InboxTab;
|
|
|
13
13
|
InboxTab[InboxTab["ALERTS_NEW"] = 0] = "ALERTS_NEW";
|
|
14
14
|
InboxTab[InboxTab["ALERT_HISTORY"] = 1] = "ALERT_HISTORY";
|
|
15
15
|
InboxTab[InboxTab["SUBSCRIBE"] = 2] = "SUBSCRIBE";
|
|
16
|
+
InboxTab[InboxTab["SOUNDS"] = 3] = "SOUNDS";
|
|
16
17
|
})(InboxTab || (InboxTab = {}));
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { __awaiter, __decorate } from "tslib";
|
|
2
|
+
import { showNotification } from '@genesislcap/foundation-notifications';
|
|
3
|
+
import { html, observable, ref, when } from '@genesislcap/web-core';
|
|
4
|
+
import { AlertSoundPlayer, base64ToBlob, blobToBase64, formatBytes, MAX_SOUND_FILE_BYTES, resolveAudioMimeType, } from '../../utils/alert-sound';
|
|
5
|
+
function showAlertSoundNotice(title, body) {
|
|
6
|
+
showNotification({
|
|
7
|
+
title,
|
|
8
|
+
body,
|
|
9
|
+
config: {
|
|
10
|
+
snackbar: {
|
|
11
|
+
type: 'error',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}, 'rapid');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Shared Attach/Replace/Test/Clear UI for rule and template dialogs.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export const alertSoundAttachmentTemplate = html `
|
|
21
|
+
<div class="alert-sound">
|
|
22
|
+
<input
|
|
23
|
+
${ref('fileInput')}
|
|
24
|
+
class="file-input"
|
|
25
|
+
type="file"
|
|
26
|
+
accept="audio/*"
|
|
27
|
+
@change=${(x, c) => x.onAlertSoundSelected(c.event)}
|
|
28
|
+
/>
|
|
29
|
+
<label>Alert sound</label>
|
|
30
|
+
<div class="alert-sound-row">
|
|
31
|
+
<span class="alert-sound-name" title=${(x) => x.alertSoundFileName || ''}>
|
|
32
|
+
${(x) => x.alertSoundFileName || 'No sound attached'}
|
|
33
|
+
</span>
|
|
34
|
+
<rapid-button appearance="lightweight" @click=${(x) => x.openAlertSoundPicker()}>
|
|
35
|
+
${(x) => (x.hasAlertSound ? 'Replace' : 'Attach')}
|
|
36
|
+
</rapid-button>
|
|
37
|
+
${when((x) => x.hasAlertSound, html `
|
|
38
|
+
<rapid-button appearance="lightweight" @click=${(x) => x.testAttachedAlertSound()}>
|
|
39
|
+
Test
|
|
40
|
+
</rapid-button>
|
|
41
|
+
<rapid-button appearance="lightweight" @click=${(x) => x.clearAttachedAlertSound()}>
|
|
42
|
+
Clear
|
|
43
|
+
</rapid-button>
|
|
44
|
+
`)}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
`;
|
|
48
|
+
/**
|
|
49
|
+
* Mixin: pick / test / clear / load server sound + apply fields to submit payload.
|
|
50
|
+
*
|
|
51
|
+
* Subclasses must provide {@link ruleService}, {@link isAlertSoundEditMode},
|
|
52
|
+
* {@link alertSoundEntityId}, and {@link alertSoundCacheSyncMode}.
|
|
53
|
+
*
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export function AlertSoundAttachmentMixin(Base) {
|
|
57
|
+
class AlertSoundAttachment extends Base {
|
|
58
|
+
constructor() {
|
|
59
|
+
super(...arguments);
|
|
60
|
+
/** Display name of the attached / pending alert sound file. */
|
|
61
|
+
this.alertSoundFileName = '';
|
|
62
|
+
this.alertSoundMimeType = '';
|
|
63
|
+
/** True when edit loaded an existing server sound (or user attached one). */
|
|
64
|
+
this.hasAlertSound = false;
|
|
65
|
+
this.pendingAlertSoundData = null;
|
|
66
|
+
/** True when the user picked a new file this session (needs upload). */
|
|
67
|
+
this.alertSoundDirty = false;
|
|
68
|
+
this.clearAlertSound = false;
|
|
69
|
+
/** In-progress Test playback; not private — mixin return type is exported. */
|
|
70
|
+
this.testAudio = null;
|
|
71
|
+
}
|
|
72
|
+
loadExistingAlertSound(dynamicRuleId) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
if (!dynamicRuleId) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const sound = yield this.ruleService.getAlertSound(dynamicRuleId);
|
|
78
|
+
if (!(sound === null || sound === void 0 ? void 0 : sound.SOUND_DATA)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.alertSoundFileName = sound.ALERT_SOUND_FILE_NAME || 'Attached sound';
|
|
82
|
+
this.alertSoundMimeType = sound.ALERT_SOUND_MIME_TYPE || 'audio/mpeg';
|
|
83
|
+
this.pendingAlertSoundData = sound.SOUND_DATA;
|
|
84
|
+
this.hasAlertSound = true;
|
|
85
|
+
this.alertSoundDirty = false;
|
|
86
|
+
this.clearAlertSound = false;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
openAlertSoundPicker() {
|
|
90
|
+
var _a;
|
|
91
|
+
(_a = this.fileInput) === null || _a === void 0 ? void 0 : _a.click();
|
|
92
|
+
}
|
|
93
|
+
onAlertSoundSelected(event) {
|
|
94
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
var _a;
|
|
96
|
+
const input = event.target;
|
|
97
|
+
const file = (_a = input.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
98
|
+
input.value = '';
|
|
99
|
+
if (!file) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (file.size > MAX_SOUND_FILE_BYTES) {
|
|
103
|
+
showAlertSoundNotice('Sound file too large', `Sound file must be ${formatBytes(MAX_SOUND_FILE_BYTES)} or smaller.`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
this.pendingAlertSoundData = yield blobToBase64(file);
|
|
108
|
+
this.alertSoundFileName = file.name;
|
|
109
|
+
this.alertSoundMimeType = resolveAudioMimeType(file.name, file.type);
|
|
110
|
+
this.hasAlertSound = true;
|
|
111
|
+
this.alertSoundDirty = true;
|
|
112
|
+
this.clearAlertSound = false;
|
|
113
|
+
}
|
|
114
|
+
catch (_b) {
|
|
115
|
+
showAlertSoundNotice('Could not read sound file', 'The selected file could not be read.');
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
clearAttachedAlertSound() {
|
|
120
|
+
this.stopTestSound();
|
|
121
|
+
this.resetAlertSoundState();
|
|
122
|
+
this.clearAlertSound = this.isAlertSoundEditMode;
|
|
123
|
+
}
|
|
124
|
+
testAttachedAlertSound() {
|
|
125
|
+
this.stopTestSound();
|
|
126
|
+
if (!this.pendingAlertSoundData) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const blob = base64ToBlob(this.pendingAlertSoundData, this.alertSoundMimeType || 'audio/mpeg');
|
|
130
|
+
const url = URL.createObjectURL(blob);
|
|
131
|
+
const audio = new Audio(url);
|
|
132
|
+
this.testAudio = audio;
|
|
133
|
+
void audio.play().catch(() => {
|
|
134
|
+
showAlertSoundNotice('Could not play sound', 'The browser could not play this file. Try MP3 or WAV.');
|
|
135
|
+
});
|
|
136
|
+
audio.addEventListener('ended', () => {
|
|
137
|
+
URL.revokeObjectURL(url);
|
|
138
|
+
if (this.testAudio === audio) {
|
|
139
|
+
this.testAudio = null;
|
|
140
|
+
}
|
|
141
|
+
}, { once: true });
|
|
142
|
+
}
|
|
143
|
+
stopTestSound() {
|
|
144
|
+
if (!this.testAudio) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this.testAudio.pause();
|
|
148
|
+
const src = this.testAudio.src;
|
|
149
|
+
if (src.startsWith('blob:')) {
|
|
150
|
+
URL.revokeObjectURL(src);
|
|
151
|
+
}
|
|
152
|
+
this.testAudio = null;
|
|
153
|
+
}
|
|
154
|
+
resetAlertSoundState() {
|
|
155
|
+
this.alertSoundFileName = '';
|
|
156
|
+
this.alertSoundMimeType = '';
|
|
157
|
+
this.pendingAlertSoundData = null;
|
|
158
|
+
this.hasAlertSound = false;
|
|
159
|
+
this.alertSoundDirty = false;
|
|
160
|
+
this.clearAlertSound = false;
|
|
161
|
+
if (this.fileInput) {
|
|
162
|
+
this.fileInput.value = '';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** Merges pending sound / clear flag onto a create/update DETAILS object. */
|
|
166
|
+
applyAlertSoundToPayload(target) {
|
|
167
|
+
if (this.clearAlertSound) {
|
|
168
|
+
target.CLEAR_ALERT_SOUND = true;
|
|
169
|
+
}
|
|
170
|
+
else if (this.alertSoundDirty && this.pendingAlertSoundData && this.alertSoundFileName) {
|
|
171
|
+
target.ALERT_SOUND_FILE_NAME = this.alertSoundFileName;
|
|
172
|
+
target.ALERT_SOUND_MIME_TYPE = this.alertSoundMimeType;
|
|
173
|
+
target.ALERT_SOUND_DATA = this.pendingAlertSoundData;
|
|
174
|
+
}
|
|
175
|
+
return target;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Keep AlertSoundPlayer's in-memory NOTIFY_ALERT_SOUND cache in sync with
|
|
179
|
+
* what we just saved — otherwise the next alert still plays the old file.
|
|
180
|
+
*/
|
|
181
|
+
syncAlertSoundCacheAfterSave() {
|
|
182
|
+
var _a;
|
|
183
|
+
if (this.alertSoundCacheSyncMode === 'all') {
|
|
184
|
+
if (!this.clearAlertSound && !this.alertSoundDirty) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
AlertSoundPlayer.shared.clearAllServerSounds();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const id = (_a = this.alertSoundEntityId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
191
|
+
if (!id) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (this.clearAlertSound) {
|
|
195
|
+
AlertSoundPlayer.shared.clearServerSound(id);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (this.alertSoundDirty && this.pendingAlertSoundData) {
|
|
199
|
+
const mimeType = this.alertSoundMimeType || 'audio/mpeg';
|
|
200
|
+
AlertSoundPlayer.shared.setServerSound(id, {
|
|
201
|
+
blob: base64ToBlob(this.pendingAlertSoundData, mimeType),
|
|
202
|
+
fileName: this.alertSoundFileName,
|
|
203
|
+
mimeType,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
__decorate([
|
|
209
|
+
observable
|
|
210
|
+
], AlertSoundAttachment.prototype, "alertSoundFileName", void 0);
|
|
211
|
+
__decorate([
|
|
212
|
+
observable
|
|
213
|
+
], AlertSoundAttachment.prototype, "alertSoundMimeType", void 0);
|
|
214
|
+
__decorate([
|
|
215
|
+
observable
|
|
216
|
+
], AlertSoundAttachment.prototype, "hasAlertSound", void 0);
|
|
217
|
+
return AlertSoundAttachment;
|
|
218
|
+
}
|