@newtonschool/react_proctoring_library 0.0.130 → 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.
@@ -25,36 +25,40 @@ const OpenHeimdall = _ref => {
25
25
  permissionSetter,
26
26
  setPermissionErrors,
27
27
  downloadHeimdallUrl,
28
- contestDuration,
28
+ endTimeStamp,
29
29
  setShouldShowDownloadHeimdallButton,
30
30
  setIsHeimdallSuccessfullyInstalledByScript
31
31
  } = (0, _react.useContext)(proctoredContext);
32
32
  (0, _react.useEffect)(() => {
33
- var _window$electron$acti, _window$electron$acti2;
34
- (_window$electron$acti = (_window$electron$acti2 = window.electron.actions).sendFileUploadConfig) === null || _window$electron$acti === void 0 ? void 0 : _window$electron$acti.call(_window$electron$acti2, config.fileUploadConfig);
33
+ var _window$electron$acti;
34
+ (_window$electron$acti = window.electron.actions) === null || _window$electron$acti === void 0 ? void 0 : _window$electron$acti.sendFileUploadConfig(config.fileUploadConfig);
35
35
  }, [config]);
36
36
  (0, _react.useEffect)(() => {
37
- var _window$electron$acti3, _window$electron$acti4;
38
- (_window$electron$acti3 = (_window$electron$acti4 = window.electron.actions).sendExamMappingDetails) === null || _window$electron$acti3 === void 0 ? void 0 : _window$electron$acti3.call(_window$electron$acti4, contestDuration);
39
- }, [contestDuration]);
37
+ if (endTimeStamp) {
38
+ var _window$electron$acti2, _window$electron$acti3;
39
+ (_window$electron$acti2 = (_window$electron$acti3 = window.electron.actions).sendExamMappingDetails) === null || _window$electron$acti2 === void 0 ? void 0 : _window$electron$acti2.call(_window$electron$acti3, {
40
+ endTimeStamp
41
+ });
42
+ }
43
+ }, [endTimeStamp]);
40
44
  (0, _react.useEffect)(() => {
41
- var _window$electron$acti9, _window$electron$acti0, _window$electron$chec, _window$electron;
45
+ var _window$electron$acti8, _window$electron$acti9, _window$electron$chec, _window$electron;
42
46
  setAskPermissionAction(oldAskPermissionAction => _objectSpread(_objectSpread({}, oldAskPermissionAction), {}, {
43
47
  openHeimdall: () => {
44
- var _window$electron$acti5, _window$electron$acti6;
45
- return (_window$electron$acti5 = (_window$electron$acti6 = window.electron.actions).openHeimdall) === null || _window$electron$acti5 === void 0 ? void 0 : _window$electron$acti5.call(_window$electron$acti6);
48
+ var _window$electron$acti4, _window$electron$acti5;
49
+ return (_window$electron$acti4 = (_window$electron$acti5 = window.electron.actions).openHeimdall) === null || _window$electron$acti4 === void 0 ? void 0 : _window$electron$acti4.call(_window$electron$acti5);
46
50
  },
47
51
  downloadHeimdall: async () => {
48
52
  if (downloadHeimdallUrl.current) {
49
- var _window$electron$acti7, _window$electron$acti8;
50
- const res = await ((_window$electron$acti7 = (_window$electron$acti8 = window.electron.actions).downloadHeimdall) === null || _window$electron$acti7 === void 0 ? void 0 : _window$electron$acti7.call(_window$electron$acti8, downloadHeimdallUrl.current));
53
+ var _window$electron$acti6, _window$electron$acti7;
54
+ const res = await ((_window$electron$acti6 = (_window$electron$acti7 = window.electron.actions).downloadHeimdall) === null || _window$electron$acti6 === void 0 ? void 0 : _window$electron$acti6.call(_window$electron$acti7, downloadHeimdallUrl.current));
51
55
  if (res.success) {
52
56
  setIsHeimdallSuccessfullyInstalledByScript(true);
53
57
  }
54
58
  }
55
59
  }
56
60
  }));
57
- (_window$electron$acti9 = (_window$electron$acti0 = window.electron.actions).onHeimdallStatusChange) === null || _window$electron$acti9 === void 0 ? void 0 : _window$electron$acti9.call(_window$electron$acti0, status => {
61
+ (_window$electron$acti8 = (_window$electron$acti9 = window.electron.actions).onHeimdallStatusChange) === null || _window$electron$acti8 === void 0 ? void 0 : _window$electron$acti8.call(_window$electron$acti9, status => {
58
62
  try {
59
63
  permissionSetter.openHeimdall(status);
60
64
  setPermissionErrors(oldErrors => _objectSpread(_objectSpread({}, oldErrors), {}, {
@@ -33,7 +33,7 @@ const ProctoredContextApp = _ref => {
33
33
  children,
34
34
  customContext = {}
35
35
  } = _ref;
36
- const [contestDuration, setContestDuration] = (0, _react.useState)({});
36
+ const [endTimeStamp, setEndTimeStamp] = (0, _react.useState)(null);
37
37
  const [webCamDeviceId, setWebCamDeviceId] = (0, _react.useState)();
38
38
  const [screenShareDeviceId, setScreenShareDeviceId] = (0, _react.useState)();
39
39
  const [audioPermission, setAudioPermission] = (0, _react.useState)();
@@ -82,8 +82,8 @@ const ProctoredContextApp = _ref => {
82
82
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(Context.Provider, {
83
83
  value: _objectSpread(_objectSpread({}, customContext), {}, {
84
84
  permissions,
85
- contestDuration,
86
- setContestDuration,
85
+ endTimeStamp,
86
+ setEndTimeStamp,
87
87
  allPermissionGrantedOnce,
88
88
  permissionSetter: {
89
89
  audio: setAudioPermission,
@@ -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
- allPermissionGranted,
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, allPermissionGranted]);
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, allPermissionGranted]);
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
- onValue(otherDeviceStatusRef, snapshot => {
184
- const val = snapshot.val();
185
- const lastBeat = val === null || val === void 0 ? void 0 : val.lastHeartbeatAt;
186
- const isConnected = Boolean(val === null || val === void 0 ? void 0 : val.connected);
187
- if (isConnected && typeof lastBeat === 'number' && now - lastBeat > OTHER_DEVICE_HEARTBEAT_TIMEOUT_MS) {
188
- update(otherDeviceStatusRef, {
189
- connected: false
190
- }).catch(otherErr => {
191
- console.error('::[proctoringStatusUtils] Failed to mark other device disconnected due to stale heartbeat:', otherErr);
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 () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newtonschool/react_proctoring_library",
3
- "version": "0.0.130",
3
+ "version": "0.0.132",
4
4
  "description": "Used to proctor online tests",
5
5
  "author": "ayushkagrawal,shreyachandra,weastel",
6
6
  "main": "dist/index.js",
@@ -57,4 +57,4 @@
57
57
  ".": "./dist/index.js",
58
58
  "./useCleanupPermissions": "./dist/useCleanupPermissions.js"
59
59
  }
60
- }
60
+ }