@newtonschool/react_proctoring_library 0.0.133-beta.0 → 0.0.133-beta.2
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.
|
@@ -7,18 +7,21 @@ exports.updateCurrentDeviceStatus = exports.setupDeviceStatusListeners = exports
|
|
|
7
7
|
var _defaults = require("../constants/defaults");
|
|
8
8
|
var _proctoringDebugUtils = require("./proctoringDebugUtils");
|
|
9
9
|
const HEARTBEAT_INTERVAL_MS = 5000;
|
|
10
|
-
const OTHER_DEVICE_HEARTBEAT_TIMEOUT_MS =
|
|
10
|
+
const OTHER_DEVICE_HEARTBEAT_TIMEOUT_MS = 20000;
|
|
11
|
+
const SERVER_TIMESTAMP = {
|
|
12
|
+
'.sv': 'timestamp'
|
|
13
|
+
};
|
|
11
14
|
const getErrorPayload = error => ({
|
|
12
15
|
name: (error === null || error === void 0 ? void 0 : error.name) || null,
|
|
13
16
|
message: (error === null || error === void 0 ? void 0 : error.message) || String(error)
|
|
14
17
|
});
|
|
15
18
|
const getObservedStatusPayload = function getObservedStatusPayload(status) {
|
|
16
|
-
let now = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] :
|
|
19
|
+
let now = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
17
20
|
return {
|
|
18
21
|
connected: Boolean(status === null || status === void 0 ? void 0 : status.connected),
|
|
19
22
|
allPermissionGranted: typeof (status === null || status === void 0 ? void 0 : status.allPermissionGranted) === 'boolean' ? status.allPermissionGranted : null,
|
|
20
23
|
lastHeartbeatAt: typeof (status === null || status === void 0 ? void 0 : status.lastHeartbeatAt) === 'number' ? status.lastHeartbeatAt : null,
|
|
21
|
-
heartbeatAgeMs: typeof (status === null || status === void 0 ? void 0 : status.lastHeartbeatAt) === 'number' ? now - status.lastHeartbeatAt : null,
|
|
24
|
+
heartbeatAgeMs: typeof now === 'number' && typeof (status === null || status === void 0 ? void 0 : status.lastHeartbeatAt) === 'number' ? now - status.lastHeartbeatAt : null,
|
|
22
25
|
deviceId: (status === null || status === void 0 ? void 0 : status.deviceId) || null
|
|
23
26
|
};
|
|
24
27
|
};
|
|
@@ -166,8 +169,16 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
166
169
|
const secondaryDeviceStatusRef = ref(myFirebaseDB, "".concat(_defaults.PROCTORING_STATUS_PATH, "/").concat(userUuid, "/secondaryDevice"));
|
|
167
170
|
const currentDeviceStatusRef = isSecondaryDevice ? secondaryDeviceStatusRef : primaryDeviceStatusRef;
|
|
168
171
|
const otherDeviceStatusRef = isSecondaryDevice ? primaryDeviceStatusRef : secondaryDeviceStatusRef;
|
|
172
|
+
const serverTimeOffsetRef = ref(myFirebaseDB, '.info/serverTimeOffset');
|
|
169
173
|
let latestPrimaryDeviceStatus = null;
|
|
170
174
|
let latestSecondaryDeviceStatus = null;
|
|
175
|
+
let serverTimeOffsetMs = null;
|
|
176
|
+
const getComparableNow = () => {
|
|
177
|
+
if (typeof serverTimeOffsetMs !== 'number') {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return Date.now() + serverTimeOffsetMs;
|
|
181
|
+
};
|
|
171
182
|
|
|
172
183
|
// When this client disconnects (tab close, network loss)
|
|
173
184
|
onDisconnect(currentDeviceStatusRef).update({
|
|
@@ -208,7 +219,7 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
208
219
|
deviceType,
|
|
209
220
|
observedDeviceType: 'secondary',
|
|
210
221
|
source,
|
|
211
|
-
observedStatus: getObservedStatusPayload(val)
|
|
222
|
+
observedStatus: getObservedStatusPayload(val, getComparableNow())
|
|
212
223
|
}, firebaseClient);
|
|
213
224
|
}
|
|
214
225
|
onSecondaryDeviceStatusChange(Boolean(val === null || val === void 0 ? void 0 : val.connected), val === null || val === void 0 ? void 0 : val.allPermissionGranted);
|
|
@@ -227,16 +238,22 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
227
238
|
const unsubscribeSecondary = onValue(secondaryDeviceStatusRef, snapshot => {
|
|
228
239
|
applySecondarySnapshot(snapshot);
|
|
229
240
|
});
|
|
241
|
+
const unsubscribeServerTimeOffset = onValue(serverTimeOffsetRef, snapshot => {
|
|
242
|
+
var _snapshot$val5, _snapshot$val6;
|
|
243
|
+
const offsetValue = (_snapshot$val5 = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot$val6 = snapshot.val) === null || _snapshot$val6 === void 0 ? void 0 : _snapshot$val6.call(snapshot)) !== null && _snapshot$val5 !== void 0 ? _snapshot$val5 : snapshot;
|
|
244
|
+
serverTimeOffsetMs = typeof offsetValue === 'number' ? offsetValue : serverTimeOffsetMs;
|
|
245
|
+
});
|
|
230
246
|
|
|
231
247
|
// Start heartbeat interval
|
|
232
248
|
let heartbeatIntervalId = null;
|
|
233
249
|
if (typeof window !== 'undefined' && typeof setInterval === 'function') {
|
|
234
250
|
heartbeatIntervalId = setInterval(() => {
|
|
235
|
-
const
|
|
251
|
+
const clientNow = Date.now();
|
|
252
|
+
const comparableNow = getComparableNow();
|
|
236
253
|
if (isCurrentDeviceActive) {
|
|
237
254
|
update(currentDeviceStatusRef, {
|
|
238
255
|
connected: true,
|
|
239
|
-
lastHeartbeatAt:
|
|
256
|
+
lastHeartbeatAt: SERVER_TIMESTAMP
|
|
240
257
|
}).then(() => {}).catch(error => {
|
|
241
258
|
if (!isSecondaryDevice) {
|
|
242
259
|
return;
|
|
@@ -245,7 +262,7 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
245
262
|
deviceType,
|
|
246
263
|
userUuid,
|
|
247
264
|
isCurrentDeviceActive,
|
|
248
|
-
now,
|
|
265
|
+
now: comparableNow !== null && comparableNow !== void 0 ? comparableNow : clientNow,
|
|
249
266
|
isSecondaryDevice,
|
|
250
267
|
error: getErrorPayload(error)
|
|
251
268
|
}, firebaseClient);
|
|
@@ -262,7 +279,7 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
262
279
|
(0, _proctoringDebugUtils.emitProctoringDebugLog)('secondary_current_device_marked_inactive', {
|
|
263
280
|
deviceType,
|
|
264
281
|
userUuid,
|
|
265
|
-
now,
|
|
282
|
+
now: comparableNow !== null && comparableNow !== void 0 ? comparableNow : clientNow,
|
|
266
283
|
isSecondaryDevice
|
|
267
284
|
}, firebaseClient);
|
|
268
285
|
}).catch(error => {
|
|
@@ -272,7 +289,7 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
272
289
|
(0, _proctoringDebugUtils.emitProctoringDebugLog)('secondary_device_status_write_failed', {
|
|
273
290
|
deviceType,
|
|
274
291
|
userUuid,
|
|
275
|
-
now,
|
|
292
|
+
now: comparableNow !== null && comparableNow !== void 0 ? comparableNow : clientNow,
|
|
276
293
|
payload: {
|
|
277
294
|
connected: false,
|
|
278
295
|
source: 'inactive_page_state'
|
|
@@ -287,10 +304,28 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
287
304
|
const otherDeviceStatus = isSecondaryDevice ? latestPrimaryDeviceStatus : latestSecondaryDeviceStatus;
|
|
288
305
|
const lastBeat = otherDeviceStatus === null || otherDeviceStatus === void 0 ? void 0 : otherDeviceStatus.lastHeartbeatAt;
|
|
289
306
|
const isConnected = Boolean(otherDeviceStatus === null || otherDeviceStatus === void 0 ? void 0 : otherDeviceStatus.connected);
|
|
290
|
-
if (isConnected && typeof lastBeat === 'number' &&
|
|
307
|
+
if (typeof comparableNow === 'number' && isConnected && typeof lastBeat === 'number' && comparableNow - lastBeat > OTHER_DEVICE_HEARTBEAT_TIMEOUT_MS) {
|
|
291
308
|
update(otherDeviceStatusRef, {
|
|
292
309
|
connected: false
|
|
293
|
-
}).then(() => {
|
|
310
|
+
}).then(() => {
|
|
311
|
+
const isMarkingSecondaryDeviceStale = !isSecondaryDevice;
|
|
312
|
+
if (!isMarkingSecondaryDeviceStale) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
(0, _proctoringDebugUtils.emitProctoringDebugLog)('primary_marked_secondary_inactive_due_to_stale_heartbeat', {
|
|
316
|
+
deviceType: 'primary',
|
|
317
|
+
userUuid,
|
|
318
|
+
now: comparableNow,
|
|
319
|
+
timeoutMs: OTHER_DEVICE_HEARTBEAT_TIMEOUT_MS,
|
|
320
|
+
heartbeatDiffMs: comparableNow - lastBeat,
|
|
321
|
+
payload: {
|
|
322
|
+
connected: false,
|
|
323
|
+
source: 'stale_other_device',
|
|
324
|
+
otherDeviceType: 'secondary',
|
|
325
|
+
otherDeviceStatus: getObservedStatusPayload(otherDeviceStatus, comparableNow)
|
|
326
|
+
}
|
|
327
|
+
}, firebaseClient);
|
|
328
|
+
}).catch(otherErr => {
|
|
294
329
|
const isMarkingSecondaryDeviceStale = !isSecondaryDevice;
|
|
295
330
|
if (!isMarkingSecondaryDeviceStale) {
|
|
296
331
|
return;
|
|
@@ -298,12 +333,12 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
298
333
|
(0, _proctoringDebugUtils.emitProctoringDebugLog)('secondary_device_status_write_failed', {
|
|
299
334
|
deviceType: 'secondary',
|
|
300
335
|
userUuid,
|
|
301
|
-
now,
|
|
336
|
+
now: comparableNow,
|
|
302
337
|
payload: {
|
|
303
338
|
connected: false,
|
|
304
339
|
source: 'stale_other_device',
|
|
305
340
|
otherDeviceType: 'secondary',
|
|
306
|
-
otherDeviceStatus: getObservedStatusPayload(otherDeviceStatus,
|
|
341
|
+
otherDeviceStatus: getObservedStatusPayload(otherDeviceStatus, comparableNow)
|
|
307
342
|
},
|
|
308
343
|
error: getErrorPayload(otherErr)
|
|
309
344
|
}, firebaseClient);
|
|
@@ -315,6 +350,7 @@ const setupDeviceStatusListeners = _ref2 => {
|
|
|
315
350
|
return () => {
|
|
316
351
|
unsubscribePrimary();
|
|
317
352
|
unsubscribeSecondary();
|
|
353
|
+
unsubscribeServerTimeOffset();
|
|
318
354
|
if (heartbeatIntervalId) {
|
|
319
355
|
clearInterval(heartbeatIntervalId);
|
|
320
356
|
}
|
package/package.json
CHANGED