@memberjunction/ng-mj-livekit-room 6.2.0-edge.2 → 6.2.0-edge.4

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 (42) hide show
  1. package/README.md +27 -3
  2. package/dist/lib/agent-avatar-notices.d.ts +57 -0
  3. package/dist/lib/agent-avatar-notices.d.ts.map +1 -0
  4. package/dist/lib/agent-avatar-notices.js +90 -0
  5. package/dist/lib/agent-avatar-notices.js.map +1 -0
  6. package/dist/lib/device-error-notice.d.ts +20 -0
  7. package/dist/lib/device-error-notice.d.ts.map +1 -0
  8. package/dist/lib/device-error-notice.js +62 -0
  9. package/dist/lib/device-error-notice.js.map +1 -0
  10. package/dist/lib/meetings/meeting-list.component.d.ts +30 -0
  11. package/dist/lib/meetings/meeting-list.component.d.ts.map +1 -0
  12. package/dist/lib/meetings/meeting-list.component.js +528 -0
  13. package/dist/lib/meetings/meeting-list.component.js.map +1 -0
  14. package/dist/lib/meetings/meeting-lobby.component.d.ts +25 -0
  15. package/dist/lib/meetings/meeting-lobby.component.d.ts.map +1 -0
  16. package/dist/lib/meetings/meeting-lobby.component.js +372 -0
  17. package/dist/lib/meetings/meeting-lobby.component.js.map +1 -0
  18. package/dist/lib/meetings/meeting-participant-picker.component.d.ts +43 -0
  19. package/dist/lib/meetings/meeting-participant-picker.component.d.ts.map +1 -0
  20. package/dist/lib/meetings/meeting-participant-picker.component.js +595 -0
  21. package/dist/lib/meetings/meeting-participant-picker.component.js.map +1 -0
  22. package/dist/lib/meetings/meeting-schedule-form.component.d.ts +32 -0
  23. package/dist/lib/meetings/meeting-schedule-form.component.d.ts.map +1 -0
  24. package/dist/lib/meetings/meeting-schedule-form.component.js +487 -0
  25. package/dist/lib/meetings/meeting-schedule-form.component.js.map +1 -0
  26. package/dist/lib/mj-livekit-room.component.d.ts +175 -10
  27. package/dist/lib/mj-livekit-room.component.d.ts.map +1 -1
  28. package/dist/lib/mj-livekit-room.component.js +664 -97
  29. package/dist/lib/mj-livekit-room.component.js.map +1 -1
  30. package/dist/lib/turn-state-poller.d.ts +57 -0
  31. package/dist/lib/turn-state-poller.d.ts.map +1 -0
  32. package/dist/lib/turn-state-poller.js +108 -0
  33. package/dist/lib/turn-state-poller.js.map +1 -0
  34. package/dist/lib/turn-taking-options.d.ts +63 -0
  35. package/dist/lib/turn-taking-options.d.ts.map +1 -0
  36. package/dist/lib/turn-taking-options.js +63 -0
  37. package/dist/lib/turn-taking-options.js.map +1 -0
  38. package/dist/public-api.d.ts +6 -0
  39. package/dist/public-api.d.ts.map +1 -1
  40. package/dist/public-api.js +6 -0
  41. package/dist/public-api.js.map +1 -1
  42. package/package.json +15 -8
package/README.md CHANGED
@@ -28,7 +28,7 @@ RealtimeBridgeResolver (MJServer) ──► @memberjunction/livekit-room-server
28
28
  ></mj-livekit-agent-room>
29
29
 
30
30
  <!-- Or just join an existing room -->
31
- <mj-livekit-agent-room [Mode]="'join'" [RoomName]="'support-42'" [DisplayName]="'Amith'"></mj-livekit-agent-room>
31
+ <mj-livekit-agent-room [Mode]="'join'" [RoomName]="'support-42'" [DisplayName]="'Jordan'"></mj-livekit-agent-room>
32
32
  ```
33
33
 
34
34
  ## What it adds over the generic component
@@ -39,11 +39,18 @@ RealtimeBridgeResolver (MJServer) ──► @memberjunction/livekit-room-server
39
39
  emits `SessionStarted` with the bridge id.
40
40
  - **Server-authorized recording** — `EnableRecording` wires the record button to the egress mutations.
41
41
  - **Multi-provider** — extends `BaseAngularComponent`; pass `[Provider]` to scope to a specific MJ server.
42
+ - **Saved layout** — the user's layout of the room is saved per user (see [Saved layout](#saved-layout)).
43
+ - **Avatar notices** — when an agent's bot says the agent's avatar can't be shown, the room shows one info notice per
44
+ agent per join, such as "Audio only for Sage: the avatar can't be shown in this meeting". The user can dismiss it; it
45
+ also hides after 10 seconds or when the agent leaves, and doesn't show again in that join. A reconnect is the same
46
+ join; a disconnect or "Try again" starts a new one. `AvatarNoticeLabels` (`AvatarNoticeOverrides` from
47
+ `@memberjunction/ng-realtime-media`) gives a host's own line per reason, shown as written with `{Agent}` for the
48
+ agent's name. A reason it leaves out, or one the room doesn't know, gets the stock line.
42
49
 
43
50
  ## Inputs
44
51
 
45
- `Mode` (`agent` / `join`), `AgentID`, `AgentName`, `RoomName`, `DisplayName`, `TurnMode`, `AutoStart`,
46
- plus pass-through gates: `Layout`, `Title`, `ShowHeader`, `ShowControlBar`, `ShowChat`,
52
+ `Mode` (`agent` / `join` / `preview`), `AgentID`, `AgentName`, `RoomName`, `DisplayName`, `TurnMode`, `AutoStart`,
53
+ `AvatarNoticeLabels`, plus pass-through gates: `Layout`, `Title`, `ShowHeader`, `ShowControlBar`, `ShowChat`,
47
54
  `ShowParticipantsPanel`, `EnablePinning`, `EnableLayoutSwitcher`, `EnableNoiseFilter`,
48
55
  `EnableBackgroundEffects`, `ShowAgentState`, `ShowPreJoin`, `EnableRecording`, `StartWithMicrophone`,
49
56
  `StartWithCamera`, `AgentAvatarUrl`, `E2EEPassphrase`, `E2EEWorker`, and the device-control gates.
@@ -53,6 +60,23 @@ plus pass-through gates: `Layout`, `Title`, `ShowHeader`, `ShowControlBar`, `Sho
53
60
  `SessionStarted`, `Connected`, `Disconnected`, `ParticipantJoined`, `ParticipantLeft`, `DataReceived`,
54
61
  `ErrorOccurred`.
55
62
 
63
+ ## Saved layout
64
+
65
+ The binding saves the user's room layout in `MJ: User Settings`, per user and for every room: where they moved
66
+ participants' tiles, where they put picture-in-picture boxes, and whether they hid their self-view. It loads the layout
67
+ when it starts, gives it to the room, and saves each change the room reports, through `MediaLayoutPrefs` from
68
+ `@memberjunction/ai-realtime-client/media`. The preview room (`Mode="preview"`) saves under its own keys, so trying it
69
+ never changes a meeting's layout.
70
+
71
+ | Saved | Meetings | Preview room |
72
+ |---|---|---|
73
+ | Tile moves | `mj.livekit.placement.v1` (`LIVEKIT_PLACEMENT_PREF_KEY`) | `mj.livekit.preview.placement.v1` (`LIVEKIT_PREVIEW_PLACEMENT_PREF_KEY`) |
74
+ | Picture-in-picture boxes | `mj.livekit.pip.v1` (`LIVEKIT_PIP_PREF_KEY`) | `mj.livekit.preview.pip.v1` (`LIVEKIT_PREVIEW_PIP_PREF_KEY`) |
75
+ | Self-view hidden | `mj.livekit.selfView.hidden.v1` (`LIVEKIT_SELF_VIEW_HIDDEN_PREF_KEY`) | `mj.livekit.preview.selfView.hidden.v1` (`LIVEKIT_PREVIEW_SELF_VIEW_HIDDEN_PREF_KEY`) |
76
+
77
+ Only a saved `true` hides the self-view; nothing saved, `false` or any other value shows it. Show and Reset layout save
78
+ `false`. When saving fails, the change still holds for the session.
79
+
56
80
  ## In Explorer
57
81
 
58
82
  `@memberjunction/ng-explorer-core` registers a `LiveKitRoomResource` (`DriverClass = 'LiveKitRoomResource'`)
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @fileoverview The meeting room's notice that an agent's avatar can't be shown. Everyone in the room sees it, once per
3
+ * agent per join, from the agent's bot's attribute (`LiveKitParticipantView.AvatarAudioOnly`, read by the room core).
4
+ * Its words are `ng-realtime-media`'s `AvatarNoticeText` in the meeting form, which names the agent, since a meeting
5
+ * can have several.
6
+ *
7
+ * @module @memberjunction/ng-mj-livekit-room
8
+ */
9
+ import type { LiveKitAvatarAudioOnly, LiveKitRoomState } from '@memberjunction/livekit-room-core';
10
+ /** How long a notice shows before it hides itself, in milliseconds. */
11
+ export declare const AGENT_AVATAR_NOTICE_HIDE_MS = 10000;
12
+ /** A reason the room can name (`RealtimeAvatarUnavailableReason` in `@memberjunction/ai`). */
13
+ type AvatarAudioOnlyReason = NonNullable<LiveKitAvatarAudioOnly['Reason']>;
14
+ /** A notice over the room: the agent it is about, and why its avatar can't be shown. */
15
+ export interface AgentAvatarNotice {
16
+ /** The agent's participant identity. */
17
+ Identity: string;
18
+ /** The agent's name, as the room shows it. */
19
+ AgentName: string;
20
+ /** Why, when the agent's bot gave a reason the room knows. */
21
+ Reason?: AvatarAudioOnlyReason;
22
+ }
23
+ /**
24
+ * The notices that agents' avatars can't be shown, for one meeting room. A notice shows from the first room state in
25
+ * which the agent's bot says audio only, until the user dismisses it, {@link AGENT_AVATAR_NOTICE_HIDE_MS} passes or the
26
+ * agent leaves, and not again in the same join: a reconnect is the same join, and only {@link Reset} starts a new one.
27
+ */
28
+ export declare class AgentAvatarNotices {
29
+ private readonly changed;
30
+ private readonly hideAfterMs;
31
+ /** The agents that have had their notice in this join. */
32
+ private readonly noticed;
33
+ /** The hide timer of each notice showing, by agent. */
34
+ private readonly timers;
35
+ private visible;
36
+ /**
37
+ * @param changed Called whenever the notices showing change, including when one hides itself.
38
+ * @param hideAfterMs How long a notice shows before it hides itself.
39
+ */
40
+ constructor(changed: () => void, hideAfterMs?: number);
41
+ /** The notices showing now, oldest first. */
42
+ get Visible(): readonly AgentAvatarNotice[];
43
+ /** Shows a notice for each agent newly audio only in this join, and drops the notices of agents who left. */
44
+ Update(state: LiveKitRoomState): void;
45
+ /** Hides an agent's notice; it does not show again in this join. */
46
+ Dismiss(identity: string): void;
47
+ /** Starts a new join: no notice shows now, and each agent's can show once more. */
48
+ Reset(): void;
49
+ /** Stops the hide timers, for a room that is going away. */
50
+ Dispose(): void;
51
+ /** A new notice for an agent newly audio only in this join, with its hide timer started; none for anyone else. */
52
+ private noticeFor;
53
+ private hide;
54
+ private stopTimer;
55
+ }
56
+ export {};
57
+ //# sourceMappingURL=agent-avatar-notices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-avatar-notices.d.ts","sourceRoot":"","sources":["../../src/lib/agent-avatar-notices.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAA0B,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1H,uEAAuE;AACvE,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,8FAA8F;AAC9F,KAAK,qBAAqB,GAAG,WAAW,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAE3E,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAED;;;;GAIG;AACH,qBAAa,kBAAkB;IAY3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAZ9B,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,uDAAuD;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoD;IAC3E,OAAO,CAAC,OAAO,CAAoC;IAEnD;;;OAGG;gBAEgB,OAAO,EAAE,MAAM,IAAI,EACnB,WAAW,GAAE,MAAoC;IAGpE,6CAA6C;IAC7C,IAAW,OAAO,IAAI,SAAS,iBAAiB,EAAE,CAEjD;IAED,6GAA6G;IACtG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAY5C,oEAAoE;IAC7D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMtC,mFAAmF;IAC5E,KAAK,IAAI,IAAI;IASpB,4DAA4D;IACrD,OAAO,IAAI,IAAI;IAItB,kHAAkH;IAClH,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,SAAS;CAOlB"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @fileoverview The meeting room's notice that an agent's avatar can't be shown. Everyone in the room sees it, once per
3
+ * agent per join, from the agent's bot's attribute (`LiveKitParticipantView.AvatarAudioOnly`, read by the room core).
4
+ * Its words are `ng-realtime-media`'s `AvatarNoticeText` in the meeting form, which names the agent, since a meeting
5
+ * can have several.
6
+ *
7
+ * @module @memberjunction/ng-mj-livekit-room
8
+ */
9
+ /** How long a notice shows before it hides itself, in milliseconds. */
10
+ export const AGENT_AVATAR_NOTICE_HIDE_MS = 10_000;
11
+ /**
12
+ * The notices that agents' avatars can't be shown, for one meeting room. A notice shows from the first room state in
13
+ * which the agent's bot says audio only, until the user dismisses it, {@link AGENT_AVATAR_NOTICE_HIDE_MS} passes or the
14
+ * agent leaves, and not again in the same join: a reconnect is the same join, and only {@link Reset} starts a new one.
15
+ */
16
+ export class AgentAvatarNotices {
17
+ changed;
18
+ hideAfterMs;
19
+ /** The agents that have had their notice in this join. */
20
+ noticed = new Set();
21
+ /** The hide timer of each notice showing, by agent. */
22
+ timers = new Map();
23
+ visible = [];
24
+ /**
25
+ * @param changed Called whenever the notices showing change, including when one hides itself.
26
+ * @param hideAfterMs How long a notice shows before it hides itself.
27
+ */
28
+ constructor(changed, hideAfterMs = AGENT_AVATAR_NOTICE_HIDE_MS) {
29
+ this.changed = changed;
30
+ this.hideAfterMs = hideAfterMs;
31
+ }
32
+ /** The notices showing now, oldest first. */
33
+ get Visible() {
34
+ return this.visible;
35
+ }
36
+ /** Shows a notice for each agent newly audio only in this join, and drops the notices of agents who left. */
37
+ Update(state) {
38
+ const present = new Set(state.Remote.map((p) => p.Identity));
39
+ const kept = this.visible.filter((notice) => present.has(notice.Identity));
40
+ this.visible.filter((notice) => !present.has(notice.Identity)).forEach((notice) => this.stopTimer(notice.Identity));
41
+ const added = state.Remote.flatMap((p) => this.noticeFor(p));
42
+ if (added.length === 0 && kept.length === this.visible.length) {
43
+ return;
44
+ }
45
+ this.visible = [...kept, ...added];
46
+ this.changed();
47
+ }
48
+ /** Hides an agent's notice; it does not show again in this join. */
49
+ Dismiss(identity) {
50
+ if (this.visible.some((notice) => notice.Identity === identity)) {
51
+ this.hide(identity);
52
+ }
53
+ }
54
+ /** Starts a new join: no notice shows now, and each agent's can show once more. */
55
+ Reset() {
56
+ this.noticed.clear();
57
+ [...this.timers.keys()].forEach((identity) => this.stopTimer(identity));
58
+ if (this.visible.length > 0) {
59
+ this.visible = [];
60
+ this.changed();
61
+ }
62
+ }
63
+ /** Stops the hide timers, for a room that is going away. */
64
+ Dispose() {
65
+ [...this.timers.keys()].forEach((identity) => this.stopTimer(identity));
66
+ }
67
+ /** A new notice for an agent newly audio only in this join, with its hide timer started; none for anyone else. */
68
+ noticeFor(participant) {
69
+ const audioOnly = participant.AvatarAudioOnly;
70
+ if (participant.Role !== 'agent' || !audioOnly || this.noticed.has(participant.Identity)) {
71
+ return [];
72
+ }
73
+ this.noticed.add(participant.Identity);
74
+ this.timers.set(participant.Identity, setTimeout(() => this.hide(participant.Identity), this.hideAfterMs));
75
+ return [{ Identity: participant.Identity, AgentName: participant.DisplayName, ...(audioOnly.Reason ? { Reason: audioOnly.Reason } : {}) }];
76
+ }
77
+ hide(identity) {
78
+ this.stopTimer(identity);
79
+ this.visible = this.visible.filter((notice) => notice.Identity !== identity);
80
+ this.changed();
81
+ }
82
+ stopTimer(identity) {
83
+ const timer = this.timers.get(identity);
84
+ if (timer !== undefined) {
85
+ clearTimeout(timer);
86
+ this.timers.delete(identity);
87
+ }
88
+ }
89
+ }
90
+ //# sourceMappingURL=agent-avatar-notices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-avatar-notices.js","sourceRoot":"","sources":["../../src/lib/agent-avatar-notices.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview The meeting room's notice that an agent's avatar can't be shown. Everyone in the room sees it, once per\n * agent per join, from the agent's bot's attribute (`LiveKitParticipantView.AvatarAudioOnly`, read by the room core).\n * Its words are `ng-realtime-media`'s `AvatarNoticeText` in the meeting form, which names the agent, since a meeting\n * can have several.\n *\n * @module @memberjunction/ng-mj-livekit-room\n */\n\nimport type { LiveKitAvatarAudioOnly, LiveKitParticipantView, LiveKitRoomState } from '@memberjunction/livekit-room-core';\n\n/** How long a notice shows before it hides itself, in milliseconds. */\nexport const AGENT_AVATAR_NOTICE_HIDE_MS = 10_000;\n\n/** A reason the room can name (`RealtimeAvatarUnavailableReason` in `@memberjunction/ai`). */\ntype AvatarAudioOnlyReason = NonNullable<LiveKitAvatarAudioOnly['Reason']>;\n\n/** A notice over the room: the agent it is about, and why its avatar can't be shown. */\nexport interface AgentAvatarNotice {\n /** The agent's participant identity. */\n Identity: string;\n /** The agent's name, as the room shows it. */\n AgentName: string;\n /** Why, when the agent's bot gave a reason the room knows. */\n Reason?: AvatarAudioOnlyReason;\n}\n\n/**\n * The notices that agents' avatars can't be shown, for one meeting room. A notice shows from the first room state in\n * which the agent's bot says audio only, until the user dismisses it, {@link AGENT_AVATAR_NOTICE_HIDE_MS} passes or the\n * agent leaves, and not again in the same join: a reconnect is the same join, and only {@link Reset} starts a new one.\n */\nexport class AgentAvatarNotices {\n /** The agents that have had their notice in this join. */\n private readonly noticed = new Set<string>();\n /** The hide timer of each notice showing, by agent. */\n private readonly timers = new Map<string, ReturnType<typeof setTimeout>>();\n private visible: readonly AgentAvatarNotice[] = [];\n\n /**\n * @param changed Called whenever the notices showing change, including when one hides itself.\n * @param hideAfterMs How long a notice shows before it hides itself.\n */\n constructor(\n private readonly changed: () => void,\n private readonly hideAfterMs: number = AGENT_AVATAR_NOTICE_HIDE_MS,\n ) {}\n\n /** The notices showing now, oldest first. */\n public get Visible(): readonly AgentAvatarNotice[] {\n return this.visible;\n }\n\n /** Shows a notice for each agent newly audio only in this join, and drops the notices of agents who left. */\n public Update(state: LiveKitRoomState): void {\n const present = new Set(state.Remote.map((p) => p.Identity));\n const kept = this.visible.filter((notice) => present.has(notice.Identity));\n this.visible.filter((notice) => !present.has(notice.Identity)).forEach((notice) => this.stopTimer(notice.Identity));\n const added = state.Remote.flatMap((p) => this.noticeFor(p));\n if (added.length === 0 && kept.length === this.visible.length) {\n return;\n }\n this.visible = [...kept, ...added];\n this.changed();\n }\n\n /** Hides an agent's notice; it does not show again in this join. */\n public Dismiss(identity: string): void {\n if (this.visible.some((notice) => notice.Identity === identity)) {\n this.hide(identity);\n }\n }\n\n /** Starts a new join: no notice shows now, and each agent's can show once more. */\n public Reset(): void {\n this.noticed.clear();\n [...this.timers.keys()].forEach((identity) => this.stopTimer(identity));\n if (this.visible.length > 0) {\n this.visible = [];\n this.changed();\n }\n }\n\n /** Stops the hide timers, for a room that is going away. */\n public Dispose(): void {\n [...this.timers.keys()].forEach((identity) => this.stopTimer(identity));\n }\n\n /** A new notice for an agent newly audio only in this join, with its hide timer started; none for anyone else. */\n private noticeFor(participant: LiveKitParticipantView): AgentAvatarNotice[] {\n const audioOnly = participant.AvatarAudioOnly;\n if (participant.Role !== 'agent' || !audioOnly || this.noticed.has(participant.Identity)) {\n return [];\n }\n this.noticed.add(participant.Identity);\n this.timers.set(participant.Identity, setTimeout(() => this.hide(participant.Identity), this.hideAfterMs));\n return [{ Identity: participant.Identity, AgentName: participant.DisplayName, ...(audioOnly.Reason ? { Reason: audioOnly.Reason } : {}) }];\n }\n\n private hide(identity: string): void {\n this.stopTimer(identity);\n this.visible = this.visible.filter((notice) => notice.Identity !== identity);\n this.changed();\n }\n\n private stopTimer(identity: string): void {\n const timer = this.timers.get(identity);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.timers.delete(identity);\n }\n }\n}\n"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @fileoverview The meeting room's notice for a device error: a microphone, camera, speaker or share that failed, or
3
+ * that the browser refused. Its line says what the user asked for and, after a colon, the reason the browser, LiveKit or
4
+ * the share picker gave, as the room's notice about what the agent sees does ("Couldn't change what the agent sees: …").
5
+ * For a panel share where the user picked another surface, the reason is `/media`'s own hint, so the line reads
6
+ * "Couldn't start sharing: To share only the panel, choose this tab in the browser's picker." A share picker the user
7
+ * closed is no error, so nothing shows for it.
8
+ *
9
+ * @module @memberjunction/ng-mj-livekit-room
10
+ */
11
+ import type { LiveKitRoomError } from '@memberjunction/livekit-room-core';
12
+ /**
13
+ * The meeting room's notice line for a room error: for a `device` error, what the user asked for and the reason given,
14
+ * or what they asked for alone when no reason was given; `null` for any other kind of error.
15
+ *
16
+ * @param error The error the room reported.
17
+ * @returns The line, such as "Couldn't turn on your camera: Could not start video source", or `null`.
18
+ */
19
+ export declare function DeviceErrorNoticeText(error: LiveKitRoomError): string | null;
20
+ //# sourceMappingURL=device-error-notice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-error-notice.d.ts","sourceRoot":"","sources":["../../src/lib/device-error-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAoC9F;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAO5E"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @fileoverview The meeting room's notice for a device error: a microphone, camera, speaker or share that failed, or
3
+ * that the browser refused. Its line says what the user asked for and, after a colon, the reason the browser, LiveKit or
4
+ * the share picker gave, as the room's notice about what the agent sees does ("Couldn't change what the agent sees: …").
5
+ * For a panel share where the user picked another surface, the reason is `/media`'s own hint, so the line reads
6
+ * "Couldn't start sharing: To share only the panel, choose this tab in the browser's picker." A share picker the user
7
+ * closed is no error, so nothing shows for it.
8
+ *
9
+ * @module @memberjunction/ng-mj-livekit-room
10
+ */
11
+ /** How the notice starts: what the user asked for, by device and request. */
12
+ const LEADS = {
13
+ microphone: {
14
+ on: "Couldn't turn on your microphone",
15
+ off: "Couldn't turn off your microphone",
16
+ switch: "Couldn't switch your microphone",
17
+ unknown: "Couldn't use your microphone",
18
+ },
19
+ camera: {
20
+ on: "Couldn't turn on your camera",
21
+ off: "Couldn't turn off your camera",
22
+ switch: "Couldn't switch your camera",
23
+ unknown: "Couldn't use your camera",
24
+ },
25
+ screen: {
26
+ on: "Couldn't start sharing",
27
+ off: "Couldn't stop sharing",
28
+ switch: "Couldn't share",
29
+ unknown: "Couldn't share",
30
+ },
31
+ speaker: {
32
+ on: "Couldn't turn on the sound",
33
+ off: "Couldn't turn off the sound",
34
+ switch: "Couldn't switch your speaker",
35
+ unknown: "Couldn't play the sound",
36
+ },
37
+ };
38
+ /** How the notice starts for a device error about no one device (listing the devices). */
39
+ const NO_DEVICE_LEAD = "Couldn't use your devices";
40
+ /**
41
+ * The meeting room's notice line for a room error: for a `device` error, what the user asked for and the reason given,
42
+ * or what they asked for alone when no reason was given; `null` for any other kind of error.
43
+ *
44
+ * @param error The error the room reported.
45
+ * @returns The line, such as "Couldn't turn on your camera: Could not start video source", or `null`.
46
+ */
47
+ export function DeviceErrorNoticeText(error) {
48
+ if (error.Kind !== 'device') {
49
+ return null;
50
+ }
51
+ const lead = error.Device ? LEADS[error.Device.Media][error.Device.Change ?? 'unknown'] : NO_DEVICE_LEAD;
52
+ const reason = reasonOf(error.Cause);
53
+ return reason ? `${lead}: ${reason}` : `${lead}.`;
54
+ }
55
+ /** The reason an error's cause gives: an error's message, or the text the preview room passes; empty for anything else. */
56
+ function reasonOf(cause) {
57
+ if (cause instanceof Error) {
58
+ return cause.message.trim();
59
+ }
60
+ return typeof cause === 'string' ? cause.trim() : '';
61
+ }
62
+ //# sourceMappingURL=device-error-notice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-error-notice.js","sourceRoot":"","sources":["../../src/lib/device-error-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,6EAA6E;AAC7E,MAAM,KAAK,GAA2F;IACpG,UAAU,EAAE;QACV,EAAE,EAAE,kCAAkC;QACtC,GAAG,EAAE,mCAAmC;QACxC,MAAM,EAAE,iCAAiC;QACzC,OAAO,EAAE,8BAA8B;KACxC;IACD,MAAM,EAAE;QACN,EAAE,EAAE,8BAA8B;QAClC,GAAG,EAAE,+BAA+B;QACpC,MAAM,EAAE,6BAA6B;QACrC,OAAO,EAAE,0BAA0B;KACpC;IACD,MAAM,EAAE;QACN,EAAE,EAAE,wBAAwB;QAC5B,GAAG,EAAE,uBAAuB;QAC5B,MAAM,EAAE,gBAAgB;QACxB,OAAO,EAAE,gBAAgB;KAC1B;IACD,OAAO,EAAE;QACP,EAAE,EAAE,4BAA4B;QAChC,GAAG,EAAE,6BAA6B;QAClC,MAAM,EAAE,8BAA8B;QACtC,OAAO,EAAE,yBAAyB;KACnC;CACF,CAAC;AAEF,0FAA0F;AAC1F,MAAM,cAAc,GAAG,2BAA2B,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAuB;IAC3D,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IACzG,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;AACpD,CAAC;AAED,2HAA2H;AAC3H,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACvD,CAAC","sourcesContent":["/**\n * @fileoverview The meeting room's notice for a device error: a microphone, camera, speaker or share that failed, or\n * that the browser refused. Its line says what the user asked for and, after a colon, the reason the browser, LiveKit or\n * the share picker gave, as the room's notice about what the agent sees does (\"Couldn't change what the agent sees: …\").\n * For a panel share where the user picked another surface, the reason is `/media`'s own hint, so the line reads\n * \"Couldn't start sharing: To share only the panel, choose this tab in the browser's picker.\" A share picker the user\n * closed is no error, so nothing shows for it.\n *\n * @module @memberjunction/ng-mj-livekit-room\n */\n\nimport type { LiveKitErrorDevice, LiveKitRoomError } from '@memberjunction/livekit-room-core';\n\n/** What the user asked of a device, or `'unknown'` when the room can't tell. */\ntype DeviceRequest = NonNullable<LiveKitErrorDevice['Change']> | 'unknown';\n\n/** How the notice starts: what the user asked for, by device and request. */\nconst LEADS: Readonly<Record<LiveKitErrorDevice['Media'], Readonly<Record<DeviceRequest, string>>>> = {\n microphone: {\n on: \"Couldn't turn on your microphone\",\n off: \"Couldn't turn off your microphone\",\n switch: \"Couldn't switch your microphone\",\n unknown: \"Couldn't use your microphone\",\n },\n camera: {\n on: \"Couldn't turn on your camera\",\n off: \"Couldn't turn off your camera\",\n switch: \"Couldn't switch your camera\",\n unknown: \"Couldn't use your camera\",\n },\n screen: {\n on: \"Couldn't start sharing\",\n off: \"Couldn't stop sharing\",\n switch: \"Couldn't share\",\n unknown: \"Couldn't share\",\n },\n speaker: {\n on: \"Couldn't turn on the sound\",\n off: \"Couldn't turn off the sound\",\n switch: \"Couldn't switch your speaker\",\n unknown: \"Couldn't play the sound\",\n },\n};\n\n/** How the notice starts for a device error about no one device (listing the devices). */\nconst NO_DEVICE_LEAD = \"Couldn't use your devices\";\n\n/**\n * The meeting room's notice line for a room error: for a `device` error, what the user asked for and the reason given,\n * or what they asked for alone when no reason was given; `null` for any other kind of error.\n *\n * @param error The error the room reported.\n * @returns The line, such as \"Couldn't turn on your camera: Could not start video source\", or `null`.\n */\nexport function DeviceErrorNoticeText(error: LiveKitRoomError): string | null {\n if (error.Kind !== 'device') {\n return null;\n }\n const lead = error.Device ? LEADS[error.Device.Media][error.Device.Change ?? 'unknown'] : NO_DEVICE_LEAD;\n const reason = reasonOf(error.Cause);\n return reason ? `${lead}: ${reason}` : `${lead}.`;\n}\n\n/** The reason an error's cause gives: an error's message, or the text the preview room passes; empty for anything else. */\nfunction reasonOf(cause: unknown): string {\n if (cause instanceof Error) {\n return cause.message.trim();\n }\n return typeof cause === 'string' ? cause.trim() : '';\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import { EventEmitter, OnInit } from '@angular/core';
2
+ import type { IMetadataProvider } from '@memberjunction/core';
3
+ import { type MeetingInfo } from '@memberjunction/graphql-dataprovider';
4
+ import * as i0 from "@angular/core";
5
+ export declare class MJMeetingListComponent implements OnInit {
6
+ private readonly cdr;
7
+ Provider?: IMetadataProvider;
8
+ JoinMeeting: EventEmitter<MeetingInfo>;
9
+ ScheduleRequested: EventEmitter<void>;
10
+ EditMeetingRequested: EventEmitter<MeetingInfo>;
11
+ Meetings: MeetingInfo[];
12
+ FilteredMeetings: MeetingInfo[];
13
+ ActiveFilter: 'all' | 'live' | 'scheduled' | 'past';
14
+ SearchQuery: string;
15
+ Loading: boolean;
16
+ ErrorMessage: string | null;
17
+ ngOnInit(): void;
18
+ LoadMeetings(): Promise<void>;
19
+ SetFilter(filter: 'all' | 'live' | 'scheduled' | 'past'): void;
20
+ OnSearchChange(): void;
21
+ ApplyFilter(): void;
22
+ OnJoin(meeting: MeetingInfo): void;
23
+ OnSchedule(): void;
24
+ OnEdit(meeting: MeetingInfo): void;
25
+ OnCancelMeeting(meeting: MeetingInfo): Promise<void>;
26
+ FormatTime(dateStr?: string): string;
27
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJMeetingListComponent, never>;
28
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJMeetingListComponent, "mj-meeting-list", never, { "Provider": { "alias": "Provider"; "required": false; }; }, { "JoinMeeting": "JoinMeeting"; "ScheduleRequested": "ScheduleRequested"; "EditMeetingRequested": "EditMeetingRequested"; }, never, never, true, never>;
29
+ }
30
+ //# sourceMappingURL=meeting-list.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meeting-list.component.d.ts","sourceRoot":"","sources":["../../../src/lib/meetings/meeting-list.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyD,YAAY,EAAS,MAAM,EAAkB,MAAM,eAAe,CAAC;AAGnI,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sCAAsC,CAAC;;AAE9C,qBAiaa,sBAAuB,YAAW,MAAM;IACnD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAiD;IAE5D,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAE5B,WAAW,4BAAmC;IAC9C,iBAAiB,qBAA4B;IAC7C,oBAAoB,4BAAmC;IAE1D,QAAQ,EAAE,WAAW,EAAE,CAAM;IAC7B,gBAAgB,EAAE,WAAW,EAAE,CAAM;IACrC,YAAY,EAAE,KAAK,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAS;IAC5D,WAAW,EAAE,MAAM,CAAM;IACzB,OAAO,EAAE,OAAO,CAAS;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C,QAAQ,IAAI,IAAI;IAIH,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBnC,SAAS,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,IAAI;IAK9D,cAAc,IAAI,IAAI;IAItB,WAAW,IAAI,IAAI;IAyBnB,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIlC,UAAU,IAAI,IAAI;IAIlB,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAI5B,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB1D,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;yCApGhC,sBAAsB;2CAAtB,sBAAsB;CAkHlC"}