@webitel/ui-sdk 26.6.31 → 26.6.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "26.6.31",
3
+ "version": "26.6.33",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "npm run docs:dev",
@@ -1,7 +1,7 @@
1
1
  export const toArray = (value) =>
2
2
  Array.isArray(value)
3
3
  ? value
4
- : !!value
4
+ : value
5
5
  ? [
6
6
  value,
7
7
  ]
@@ -1,4 +1,4 @@
1
- export const QueueType = Object.freeze({
1
+ export const QueueType = {
2
2
  OFFLINE_QUEUE: 0,
3
3
  INBOUND_QUEUE: 1,
4
4
  OUTBOUND_IVR_QUEUE: 2,
@@ -8,7 +8,10 @@ export const QueueType = Object.freeze({
8
8
  CHAT_INBOUND_QUEUE: 6,
9
9
  INBOUND_JOB_QUEUE: 7,
10
10
  OUTBOUND_JOB_QUEUE: 8,
11
- });
11
+ INBOUND_IM_CHAT_QUEUE: 9,
12
+ } as const;
13
+
14
+ export type QueueType = (typeof QueueType)[keyof typeof QueueType];
12
15
 
13
16
  export const QueueTypeName = {
14
17
  OFFLINE_QUEUE: 'offline',
@@ -21,7 +24,8 @@ export const QueueTypeName = {
21
24
  INBOUND_JOB_QUEUE: 'task',
22
25
  OUTBOUND_JOB_QUEUE: 'outbound_task',
23
26
  OUTBOUND_CALL: 'outbound_call',
27
+ INBOUND_IM_CHAT_QUEUE: 'inbound_im_chat',
24
28
  NOT_IMPLEMENT: 'NOT_IMPLEMENT',
25
- };
29
+ } as const;
26
30
 
27
31
  export type QueueTypeName = (typeof QueueTypeName)[keyof typeof QueueTypeName];
@@ -217,6 +217,7 @@ export default deepmerge(
217
217
  [QueueType.CHAT_INBOUND_QUEUE]: 'Chat queue',
218
218
  [QueueType.INBOUND_JOB_QUEUE]: 'Inbound task queue',
219
219
  [QueueType.OUTBOUND_JOB_QUEUE]: 'Outbound task queue',
220
+ [QueueType.INBOUND_IM_CHAT_QUEUE]: 'im queue',
220
221
  },
221
222
  },
222
223
  agent: {
@@ -1,4 +1,4 @@
1
- import { computed, onUnmounted, ref } from 'vue';
1
+ import { computed, onBeforeUnmount, ref } from 'vue';
2
2
 
3
3
  import type { DocumentPiPWindow, MediaSnapshot } from '../../types/types';
4
4
 
@@ -21,6 +21,25 @@ export function useDocumentPiP(
21
21
  let originalParent: Node | null = null;
22
22
  let originalNextSibling: Node | null = null;
23
23
 
24
+ /**
25
+ * @author @Oleksandr Palonnyi
26
+ *
27
+ * [WTEL-9774](https://webitel.atlassian.net/browse/WTEL-9774)
28
+ *
29
+ * True while `api.requestWindow()` is in flight.
30
+ * Prevents a second `enterPiP` from starting while the first async
31
+ * request is still pending.
32
+ */
33
+ let isRequestWindowPending = false;
34
+
35
+ /**
36
+ * Set in `onBeforeUnmount`. Used as the abort sentinel for the
37
+ * post-`requestWindow` guard instead of checking `!originalParent`,
38
+ * because a legitimately detached-but-in-document element also has
39
+ * `parentNode === null` and must not be treated as an unmount signal.
40
+ */
41
+ let isUnmounting = false;
42
+
24
43
  const mediaSnapshot: MediaSnapshot[] = [];
25
44
  const { retryPlayback, stopPlaybackRetry } =
26
45
  createPlaybackRetry(mediaSnapshot);
@@ -42,18 +61,12 @@ export function useDocumentPiP(
42
61
  };
43
62
 
44
63
  const restoreElement = () => {
45
- if (!movedEl || !originalParent) {
64
+ if (!movedEl) {
46
65
  mediaSnapshot.splice(0);
47
- movedEl = null;
48
- originalParent = null;
49
- originalNextSibling = null;
50
66
  return;
51
67
  }
52
68
 
53
- if (
54
- originalNextSibling &&
55
- originalNextSibling.parentNode === originalParent
56
- ) {
69
+ if (originalParent) {
57
70
  originalParent.insertBefore(movedEl, originalNextSibling);
58
71
  }
59
72
 
@@ -61,8 +74,6 @@ export function useDocumentPiP(
61
74
  resumePlayback(movedEl, mediaSnapshot);
62
75
 
63
76
  movedEl = null;
64
- originalParent = null;
65
- originalNextSibling = null;
66
77
  };
67
78
 
68
79
  const onPiPWindowClose = () => {
@@ -74,13 +85,36 @@ export function useDocumentPiP(
74
85
  };
75
86
 
76
87
  const enterPiP = async (width = 480, height = 320) => {
77
- if (!isSupported.value || isPiP.value) return;
88
+ if (!isSupported.value || isPiP.value || isRequestWindowPending) return;
78
89
 
79
90
  const el = getElement();
80
91
  if (!el) return;
81
92
 
93
+ /**
94
+ * @author @Oleksandr Palonnyi
95
+ *
96
+ * [WTEL-9774](https://webitel.atlassian.net/browse/WTEL-9774)
97
+ *
98
+ * If the element is stranded inside a closed Document PiP window (e.g. when
99
+ * the consuming app renders the component into a PiP context), rescue it back
100
+ * to the main document before proceeding. Using `document.body` as a temporary
101
+ * parent is safe: the element is `position:fixed` so its visual position is
102
+ * viewport-relative regardless of where it sits in the DOM tree, and Vue will
103
+ * remove it from `document.body` when the component eventually unmounts.
104
+ */
105
+ if (el.ownerDocument !== document) {
106
+ document.body.appendChild(el);
107
+ }
108
+
109
+ if (!originalParent) {
110
+ originalParent = el.parentNode;
111
+ originalNextSibling = el.nextSibling;
112
+ }
113
+
82
114
  const api = (window as DocumentPiPWindow).documentPictureInPicture;
83
115
  let win: Window;
116
+
117
+ isRequestWindowPending = true;
84
118
  try {
85
119
  win = await api.requestWindow({
86
120
  width,
@@ -88,6 +122,24 @@ export function useDocumentPiP(
88
122
  });
89
123
  } catch {
90
124
  return;
125
+ } finally {
126
+ isRequestWindowPending = false;
127
+ }
128
+
129
+ /**
130
+ * @author @Oleksandr Palonnyi
131
+ *
132
+ * [WTEL-9774](https://webitel.atlassian.net/browse/WTEL-9774)
133
+ *
134
+ * `onBeforeUnmount` can fire while `requestWindow` is awaited above.
135
+ * Abort if that happened so we don't open a PiP window that can never
136
+ * restore its element. We use `isUnmounting` rather than `!originalParent`
137
+ * because a legitimately detached element also has `parentNode === null`
138
+ * and must not be treated as an unmount signal.
139
+ */
140
+ if (isUnmounting) {
141
+ win.close();
142
+ return;
91
143
  }
92
144
 
93
145
  pipWindow = win;
@@ -97,10 +149,7 @@ export function useDocumentPiP(
97
149
  'margin:0;overflow:hidden;width:100%;height:100%;';
98
150
  bridgeCustomElements(el, win);
99
151
 
100
- originalParent = el.parentNode;
101
- originalNextSibling = el.nextSibling;
102
152
  movedEl = el;
103
-
104
153
  snapshotMedia(el, mediaSnapshot);
105
154
  win.document.body.appendChild(el);
106
155
 
@@ -131,8 +180,11 @@ export function useDocumentPiP(
131
180
  isPiP.value = false;
132
181
  };
133
182
 
134
- onUnmounted(() => {
183
+ onBeforeUnmount(() => {
184
+ isUnmounting = true;
135
185
  if (isPiP.value) exitPiP();
186
+ originalParent = null;
187
+ originalNextSibling = null;
136
188
  });
137
189
 
138
190
  return {
@@ -1,25 +1,28 @@
1
- export declare const QueueType: Readonly<{
2
- OFFLINE_QUEUE: 0;
3
- INBOUND_QUEUE: 1;
4
- OUTBOUND_IVR_QUEUE: 2;
5
- PREVIEW_DIALER: 3;
6
- PROGRESSIVE_DIALER: 4;
7
- PREDICTIVE_DIALER: 5;
8
- CHAT_INBOUND_QUEUE: 6;
9
- INBOUND_JOB_QUEUE: 7;
10
- OUTBOUND_JOB_QUEUE: 8;
11
- }>;
1
+ export declare const QueueType: {
2
+ readonly OFFLINE_QUEUE: 0;
3
+ readonly INBOUND_QUEUE: 1;
4
+ readonly OUTBOUND_IVR_QUEUE: 2;
5
+ readonly PREVIEW_DIALER: 3;
6
+ readonly PROGRESSIVE_DIALER: 4;
7
+ readonly PREDICTIVE_DIALER: 5;
8
+ readonly CHAT_INBOUND_QUEUE: 6;
9
+ readonly INBOUND_JOB_QUEUE: 7;
10
+ readonly OUTBOUND_JOB_QUEUE: 8;
11
+ readonly INBOUND_IM_CHAT_QUEUE: 9;
12
+ };
13
+ export type QueueType = (typeof QueueType)[keyof typeof QueueType];
12
14
  export declare const QueueTypeName: {
13
- OFFLINE_QUEUE: string;
14
- INBOUND_QUEUE: string;
15
- OUTBOUND_IVR_QUEUE: string;
16
- PREVIEW_DIALER: string;
17
- PROGRESSIVE_DIALER: string;
18
- PREDICTIVE_DIALER: string;
19
- CHAT_INBOUND_QUEUE: string;
20
- INBOUND_JOB_QUEUE: string;
21
- OUTBOUND_JOB_QUEUE: string;
22
- OUTBOUND_CALL: string;
23
- NOT_IMPLEMENT: string;
15
+ readonly OFFLINE_QUEUE: "offline";
16
+ readonly INBOUND_QUEUE: "inbound";
17
+ readonly OUTBOUND_IVR_QUEUE: "ivr";
18
+ readonly PREVIEW_DIALER: "preview";
19
+ readonly PROGRESSIVE_DIALER: "progressive";
20
+ readonly PREDICTIVE_DIALER: "predictive";
21
+ readonly CHAT_INBOUND_QUEUE: "inbound chat";
22
+ readonly INBOUND_JOB_QUEUE: "task";
23
+ readonly OUTBOUND_JOB_QUEUE: "outbound_task";
24
+ readonly OUTBOUND_CALL: "outbound_call";
25
+ readonly INBOUND_IM_CHAT_QUEUE: "inbound_im_chat";
26
+ readonly NOT_IMPLEMENT: "NOT_IMPLEMENT";
24
27
  };
25
28
  export type QueueTypeName = (typeof QueueTypeName)[keyof typeof QueueTypeName];
@@ -178,6 +178,7 @@ declare const _default: {
178
178
  6: string;
179
179
  7: string;
180
180
  8: string;
181
+ 9: string;
181
182
  };
182
183
  };
183
184
  agent: {
@@ -179,6 +179,7 @@ declare const _default: import("vue-i18n").I18n<{
179
179
  6: string;
180
180
  7: string;
181
181
  8: string;
182
+ 9: string;
182
183
  };
183
184
  };
184
185
  agent: {
@@ -189,6 +189,7 @@ export declare const messages: {
189
189
  6: string;
190
190
  7: string;
191
191
  8: string;
192
+ 9: string;
192
193
  };
193
194
  };
194
195
  agent: {