@newtonschool/react_proctoring_library 0.0.131 → 0.0.132
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.
|
@@ -9,6 +9,8 @@ var _react = require("react");
|
|
|
9
9
|
var _proctoringStatusUtils = require("../../utils/proctoringStatusUtils");
|
|
10
10
|
var _permission = require("../../utils/permission");
|
|
11
11
|
var _breachUtils = require("../../utils/breachUtils");
|
|
12
|
+
var _usePageActivity = _interopRequireDefault(require("../../hooks/usePageActivity"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
14
|
const SecondaryDevice = _ref => {
|
|
13
15
|
let {
|
|
14
16
|
proctoredContext,
|
|
@@ -24,12 +26,16 @@ const SecondaryDevice = _ref => {
|
|
|
24
26
|
const [primaryDeviceOnline, setPrimaryDeviceOnline] = (0, _react.useState)(true);
|
|
25
27
|
const [secondaryDeviceOnline, setSecondaryDeviceOnline] = (0, _react.useState)(true);
|
|
26
28
|
const [secondaryDeviceAllPermissionGranted, setSecondaryDeviceAllPermissionGranted] = (0, _react.useState)(true);
|
|
29
|
+
const {
|
|
30
|
+
isActive: isCurrentDeviceActive
|
|
31
|
+
} = (0, _usePageActivity.default)();
|
|
27
32
|
const allPermissionGranted = (0, _permission.hasAllPermissions)((0, _breachUtils.getRequiredPermissionsFromProctorParams)(proctorParams), permissions);
|
|
33
|
+
const effectiveAllPermissionGranted = allPermissionGranted && isCurrentDeviceActive;
|
|
28
34
|
(0, _react.useEffect)(() => {
|
|
29
35
|
const cleanup = (0, _proctoringStatusUtils.setupDeviceStatusListeners)({
|
|
30
36
|
firebaseClient,
|
|
31
37
|
isSecondaryDevice,
|
|
32
|
-
|
|
38
|
+
isCurrentDeviceActive,
|
|
33
39
|
onPrimaryDeviceStatusChange: connectionStatus => {
|
|
34
40
|
setPrimaryDeviceOnline(connectionStatus);
|
|
35
41
|
},
|
|
@@ -39,21 +45,21 @@ const SecondaryDevice = _ref => {
|
|
|
39
45
|
}
|
|
40
46
|
});
|
|
41
47
|
return cleanup;
|
|
42
|
-
}, [firebaseClient, isSecondaryDevice,
|
|
48
|
+
}, [firebaseClient, isSecondaryDevice, isCurrentDeviceActive]);
|
|
43
49
|
(0, _react.useEffect)(() => {
|
|
44
50
|
(0, _proctoringStatusUtils.updateCurrentDeviceStatus)({
|
|
45
51
|
firebaseClient,
|
|
46
52
|
isSecondaryDevice,
|
|
47
|
-
allPermissionGranted
|
|
53
|
+
allPermissionGranted: effectiveAllPermissionGranted
|
|
48
54
|
});
|
|
49
|
-
}, [firebaseClient, isSecondaryDevice,
|
|
55
|
+
}, [firebaseClient, isSecondaryDevice, effectiveAllPermissionGranted]);
|
|
50
56
|
(0, _react.useEffect)(() => {
|
|
51
57
|
if (isSecondaryDevice) {
|
|
52
|
-
setSecondaryDevicePermission(secondaryDeviceOnline);
|
|
58
|
+
setSecondaryDevicePermission(secondaryDeviceOnline && isCurrentDeviceActive);
|
|
53
59
|
} else {
|
|
54
60
|
setSecondaryDevicePermission(secondaryDeviceOnline && secondaryDeviceAllPermissionGranted);
|
|
55
61
|
}
|
|
56
|
-
}, [isSecondaryDevice, primaryDeviceOnline, secondaryDeviceOnline, secondaryDeviceAllPermissionGranted, setSecondaryDevicePermission]);
|
|
62
|
+
}, [isSecondaryDevice, primaryDeviceOnline, secondaryDeviceOnline, secondaryDeviceAllPermissionGranted, isCurrentDeviceActive, setSecondaryDevicePermission]);
|
|
57
63
|
return null;
|
|
58
64
|
};
|
|
59
65
|
exports.SecondaryDevice = SecondaryDevice;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
require("core-js/modules/es.symbol.description.js");
|
|
4
|
+
require("core-js/modules/esnext.iterator.constructor.js");
|
|
5
|
+
require("core-js/modules/esnext.iterator.filter.js");
|
|
6
|
+
require("core-js/modules/esnext.iterator.for-each.js");
|
|
7
|
+
Object.defineProperty(exports, "__esModule", {
|
|
8
|
+
value: true
|
|
9
|
+
});
|
|
10
|
+
exports.default = usePageActivity;
|
|
11
|
+
require("core-js/modules/web.dom-collections.iterator.js");
|
|
12
|
+
var _react = require("react");
|
|
13
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
16
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
17
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
18
|
+
const getInitialPageActivityState = () => ({
|
|
19
|
+
isVisible: typeof document === 'undefined' ? true : !document.hidden,
|
|
20
|
+
isFocused: typeof document === 'undefined' || typeof document.hasFocus !== 'function' ? true : document.hasFocus(),
|
|
21
|
+
isLifecycleActive: true
|
|
22
|
+
});
|
|
23
|
+
function usePageActivity() {
|
|
24
|
+
const [pageActivityState, setPageActivityState] = (0, _react.useState)(getInitialPageActivityState);
|
|
25
|
+
(0, _react.useEffect)(() => {
|
|
26
|
+
if (typeof window === 'undefined') {
|
|
27
|
+
return () => {};
|
|
28
|
+
}
|
|
29
|
+
const syncPageActivity = () => {
|
|
30
|
+
setPageActivityState(previousState => _objectSpread(_objectSpread({}, previousState), {}, {
|
|
31
|
+
isVisible: !document.hidden,
|
|
32
|
+
isFocused: typeof document.hasFocus === 'function' ? document.hasFocus() : true
|
|
33
|
+
}));
|
|
34
|
+
};
|
|
35
|
+
const handleFocus = () => {
|
|
36
|
+
setPageActivityState(previousState => _objectSpread(_objectSpread({}, previousState), {}, {
|
|
37
|
+
isFocused: true,
|
|
38
|
+
isLifecycleActive: true
|
|
39
|
+
}));
|
|
40
|
+
syncPageActivity();
|
|
41
|
+
};
|
|
42
|
+
const handleBlur = () => {
|
|
43
|
+
setPageActivityState(previousState => _objectSpread(_objectSpread({}, previousState), {}, {
|
|
44
|
+
isFocused: false
|
|
45
|
+
}));
|
|
46
|
+
};
|
|
47
|
+
const handleVisibilityChange = () => {
|
|
48
|
+
setPageActivityState(previousState => _objectSpread(_objectSpread({}, previousState), {}, {
|
|
49
|
+
isVisible: !document.hidden,
|
|
50
|
+
isFocused: typeof document.hasFocus === 'function' ? document.hasFocus() : true,
|
|
51
|
+
isLifecycleActive: document.hidden ? previousState.isLifecycleActive : true
|
|
52
|
+
}));
|
|
53
|
+
};
|
|
54
|
+
const handlePageHide = () => {
|
|
55
|
+
setPageActivityState(previousState => _objectSpread(_objectSpread({}, previousState), {}, {
|
|
56
|
+
isLifecycleActive: false,
|
|
57
|
+
isFocused: false
|
|
58
|
+
}));
|
|
59
|
+
};
|
|
60
|
+
const handlePageShow = () => {
|
|
61
|
+
setPageActivityState(previousState => _objectSpread(_objectSpread({}, previousState), {}, {
|
|
62
|
+
isLifecycleActive: true
|
|
63
|
+
}));
|
|
64
|
+
syncPageActivity();
|
|
65
|
+
};
|
|
66
|
+
syncPageActivity();
|
|
67
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
68
|
+
window.addEventListener('focus', handleFocus);
|
|
69
|
+
window.addEventListener('blur', handleBlur);
|
|
70
|
+
window.addEventListener('pagehide', handlePageHide);
|
|
71
|
+
window.addEventListener('pageshow', handlePageShow);
|
|
72
|
+
if ('onfreeze' in document) {
|
|
73
|
+
document.addEventListener('freeze', handlePageHide);
|
|
74
|
+
document.addEventListener('resume', handlePageShow);
|
|
75
|
+
}
|
|
76
|
+
return () => {
|
|
77
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
78
|
+
window.removeEventListener('focus', handleFocus);
|
|
79
|
+
window.removeEventListener('blur', handleBlur);
|
|
80
|
+
window.removeEventListener('pagehide', handlePageHide);
|
|
81
|
+
window.removeEventListener('pageshow', handlePageShow);
|
|
82
|
+
if ('onfreeze' in document) {
|
|
83
|
+
document.removeEventListener('freeze', handlePageHide);
|
|
84
|
+
document.removeEventListener('resume', handlePageShow);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}, []);
|
|
88
|
+
return (0, _react.useMemo)(() => _objectSpread(_objectSpread({}, pageActivityState), {}, {
|
|
89
|
+
isActive: pageActivityState.isVisible && pageActivityState.isFocused && pageActivityState.isLifecycleActive
|
|
90
|
+
}), [pageActivityState]);
|
|
91
|
+
}
|
|
@@ -110,7 +110,8 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
110
110
|
firebaseClient,
|
|
111
111
|
isSecondaryDevice,
|
|
112
112
|
onPrimaryDeviceStatusChange,
|
|
113
|
-
onSecondaryDeviceStatusChange
|
|
113
|
+
onSecondaryDeviceStatusChange,
|
|
114
|
+
isCurrentDeviceActive = true
|
|
114
115
|
} = _ref2;
|
|
115
116
|
const {
|
|
116
117
|
userUuid,
|
|
@@ -129,6 +130,8 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
129
130
|
const secondaryDeviceStatusRef = ref(myFirebaseDB, "".concat(_defaults.PROCTORING_STATUS_PATH, "/").concat(userUuid, "/secondaryDevice"));
|
|
130
131
|
const currentDeviceStatusRef = isSecondaryDevice ? secondaryDeviceStatusRef : primaryDeviceStatusRef;
|
|
131
132
|
const otherDeviceStatusRef = isSecondaryDevice ? primaryDeviceStatusRef : secondaryDeviceStatusRef;
|
|
133
|
+
let latestPrimaryDeviceStatus = null;
|
|
134
|
+
let latestSecondaryDeviceStatus = null;
|
|
132
135
|
|
|
133
136
|
// When this client disconnects (tab close, network loss)
|
|
134
137
|
onDisconnect(currentDeviceStatusRef).update({
|
|
@@ -141,11 +144,13 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
141
144
|
const applyPrimarySnapshot = snapshot => {
|
|
142
145
|
var _snapshot$val, _snapshot$val2;
|
|
143
146
|
const val = (_snapshot$val = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot$val2 = snapshot.val) === null || _snapshot$val2 === void 0 ? void 0 : _snapshot$val2.call(snapshot)) !== null && _snapshot$val !== void 0 ? _snapshot$val : snapshot;
|
|
147
|
+
latestPrimaryDeviceStatus = val;
|
|
144
148
|
onPrimaryDeviceStatusChange(Boolean(val === null || val === void 0 ? void 0 : val.connected));
|
|
145
149
|
};
|
|
146
150
|
const applySecondarySnapshot = snapshot => {
|
|
147
151
|
var _snapshot$val3, _snapshot$val4;
|
|
148
152
|
const val = (_snapshot$val3 = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot$val4 = snapshot.val) === null || _snapshot$val4 === void 0 ? void 0 : _snapshot$val4.call(snapshot)) !== null && _snapshot$val3 !== void 0 ? _snapshot$val3 : snapshot;
|
|
153
|
+
latestSecondaryDeviceStatus = val;
|
|
149
154
|
onSecondaryDeviceStatusChange(Boolean(val === null || val === void 0 ? void 0 : val.connected), val === null || val === void 0 ? void 0 : val.allPermissionGranted);
|
|
150
155
|
};
|
|
151
156
|
if (typeof (primaryDeviceStatusRef === null || primaryDeviceStatusRef === void 0 ? void 0 : primaryDeviceStatusRef.get) === 'function') {
|
|
@@ -168,9 +173,7 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
168
173
|
if (typeof window !== 'undefined' && typeof setInterval === 'function') {
|
|
169
174
|
heartbeatIntervalId = setInterval(() => {
|
|
170
175
|
const now = Date.now();
|
|
171
|
-
|
|
172
|
-
// Send heartbeat for current device if document is not hidden
|
|
173
|
-
if (!document.hidden) {
|
|
176
|
+
if (isCurrentDeviceActive) {
|
|
174
177
|
update(currentDeviceStatusRef, {
|
|
175
178
|
connected: true,
|
|
176
179
|
lastHeartbeatAt: now
|
|
@@ -178,20 +181,25 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
178
181
|
console.error('::[proctoringStatusUtils] Failed to send heartbeat for current device:', error);
|
|
179
182
|
});
|
|
180
183
|
}
|
|
184
|
+
if (!isCurrentDeviceActive) {
|
|
185
|
+
update(currentDeviceStatusRef, {
|
|
186
|
+
connected: false
|
|
187
|
+
}).catch(error => {
|
|
188
|
+
console.error('::[proctoringStatusUtils] Failed to mark current device inactive:', error);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
181
191
|
|
|
182
192
|
// Check other device's last heartbeat; if > OTHER_DEVICE_HEARTBEAT_TIMEOUT_MS then mark as disconnected.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
});
|
|
193
|
+
const otherDeviceStatus = isSecondaryDevice ? latestPrimaryDeviceStatus : latestSecondaryDeviceStatus;
|
|
194
|
+
const lastBeat = otherDeviceStatus === null || otherDeviceStatus === void 0 ? void 0 : otherDeviceStatus.lastHeartbeatAt;
|
|
195
|
+
const isConnected = Boolean(otherDeviceStatus === null || otherDeviceStatus === void 0 ? void 0 : otherDeviceStatus.connected);
|
|
196
|
+
if (isConnected && typeof lastBeat === 'number' && now - lastBeat > OTHER_DEVICE_HEARTBEAT_TIMEOUT_MS) {
|
|
197
|
+
update(otherDeviceStatusRef, {
|
|
198
|
+
connected: false
|
|
199
|
+
}).catch(otherErr => {
|
|
200
|
+
console.error('::[proctoringStatusUtils] Failed to mark other device disconnected due to stale heartbeat:', otherErr);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
195
203
|
}, HEARTBEAT_INTERVAL_MS);
|
|
196
204
|
}
|
|
197
205
|
return () => {
|