@webitel/ui-sdk 26.6.32 → 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
package/src/modules/CallSession/modules/VideoCall/composables/useDocumentPiP/useDocumentPiP.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed,
|
|
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
|
|
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
|
-
|
|
183
|
+
onBeforeUnmount(() => {
|
|
184
|
+
isUnmounting = true;
|
|
135
185
|
if (isPiP.value) exitPiP();
|
|
186
|
+
originalParent = null;
|
|
187
|
+
originalNextSibling = null;
|
|
136
188
|
});
|
|
137
189
|
|
|
138
190
|
return {
|