-
Notifications
You must be signed in to change notification settings - Fork 2
/
useCrossOriginPopup.tsx
123 lines (109 loc) · 3.93 KB
/
useCrossOriginPopup.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Utilities
*/
import * as React from "react";
import { useInterval } from "./useInterval";
/** Hook that will show a popup window
* @public
*/
export function useCrossOriginPopup(
visible: boolean,
url: string | undefined,
title: string,
width: number,
height: number,
onClose: () => void
) {
const [checkPopupAliveDelay, setCheckPopupAliveDelay] = React.useState<
number | undefined
>();
// ONLY re-render the popup when visibility changes, any other changes get deferred until next visibility change
// hence the massive use of 'useRef'
const popupWindow = React.useRef<Window>();
const savedUrl = React.useRef(url);
const savedTitle = React.useRef(title);
const savedWidth = React.useRef(width);
const savedHeight = React.useRef(height);
const savedOnClose = React.useRef(onClose);
React.useEffect(() => {
savedUrl.current = url;
}, [url]);
React.useEffect(() => {
savedTitle.current = title;
}, [title]);
React.useEffect(() => {
savedWidth.current = width;
}, [width]);
React.useEffect(() => {
savedHeight.current = height;
}, [height]);
React.useEffect(() => {
savedOnClose.current = onClose;
}, [onClose]);
// Cleanup method after a popup closure. Also calls the OnClose callback.
const handleClosedPopup = React.useCallback(() => {
savedOnClose.current();
setCheckPopupAliveDelay(undefined);
popupWindow.current = undefined;
}, []);
const closePopup = React.useCallback(() => {
if (popupWindow.current !== undefined) {
popupWindow.current.close(); // Manually close the popup
handleClosedPopup();
}
}, [handleClosedPopup]);
const checkPopupClosed = React.useCallback(() => {
if (popupWindow.current?.closed) {
// Popup has been closed by end-user, inform our host and cleanup
handleClosedPopup();
}
}, [handleClosedPopup]);
// Close popup when parent window get closed
React.useEffect(() => {
window.onbeforeunload = (_event) => {
closePopup();
};
return () => {
window.onbeforeunload = null;
};
}, [closePopup]);
// Whenever the hook is unloaded, make sure the underlying popup get closed.
// Note: An interval is used to check if popup was closed by user: because we access
// a cross domain resource inside the popup, we don't have access to popup window events.
// As a workaround, we periodically check if popup is still alive.
// Reference: https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128
React.useEffect(() => {
return () => {
closePopup();
};
}, [closePopup]);
// Timer that checks if popup was closed by end-user
useInterval(checkPopupClosed, checkPopupAliveDelay);
// ==> Main render effect
// Monitors visibility changes and open/close the popup accordingly.
React.useEffect(() => {
// If visible and a popup window is not already open, open a new popup window
if (visible && popupWindow.current === undefined) {
const popup = window.open(
savedUrl.current,
savedTitle.current,
`width=${savedWidth.current},height=${savedHeight.current}`
);
if (popup) {
popup.focus();
popupWindow.current = popup;
// Start and interval that will check if popup got closed by user
setCheckPopupAliveDelay(1000);
}
}
// If not visible but a previous popup window is still open, close it.
if (!visible && popupWindow.current !== undefined) {
popupWindow.current.close();
handleClosedPopup();
}
}, [handleClosedPopup, visible]);
}