@newtonschool/react_proctoring_library 0.0.1 → 0.0.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.
Files changed (65) hide show
  1. package/README.md +70 -66
  2. package/dist/assets/images/newton-school-logo.png +0 -0
  3. package/dist/assets/images/url-video-permission-highlight.png +0 -0
  4. package/dist/assets/images/vid-micro-permissions-given.png +0 -0
  5. package/dist/assets/images/vid-micro-permissions.png +0 -0
  6. package/dist/assets/videos/ask-permission.mp4 +0 -0
  7. package/dist/assets/videos/give-permissions.mp4 +0 -0
  8. package/dist/components/FullScreenPermission.js +84 -0
  9. package/dist/components/FullScreenTestInWebcam.js +22 -10
  10. package/dist/components/ProctorApp.js +92 -0
  11. package/dist/components/ResponseModal.js +24 -16
  12. package/dist/components/activity-tracker/index.js +144 -0
  13. package/dist/components/activity-tracker/index.scss +4 -0
  14. package/dist/components/full-screen-permission.scss +8 -0
  15. package/dist/components/index.js +31 -0
  16. package/dist/components/permissions/audio-video-permission.js +35 -0
  17. package/dist/components/permissions/audio-video-permission.scss +228 -0
  18. package/dist/components/permissions/blocked-permission.js +76 -0
  19. package/dist/components/permissions/full-screen-permission.js +43 -0
  20. package/dist/components/permissions/full-screen-permission.scss +8 -0
  21. package/dist/components/permissions/index.js +70 -0
  22. package/dist/components/permissions/index.scss +6 -0
  23. package/dist/components/permissions/initial-screen.js +52 -0
  24. package/dist/components/permissions/permission-body.js +53 -0
  25. package/dist/constants/defaults.js +38 -0
  26. package/dist/constants/text.js +56 -0
  27. package/dist/demo-code/README.md +70 -0
  28. package/dist/demo-code/package.json +38 -0
  29. package/dist/demo-code/public/favicon.ico +0 -0
  30. package/dist/demo-code/public/index.html +43 -0
  31. package/dist/demo-code/public/logo192.png +0 -0
  32. package/dist/demo-code/public/logo512.png +0 -0
  33. package/dist/demo-code/public/manifest.json +25 -0
  34. package/dist/demo-code/public/robots.txt +3 -0
  35. package/dist/demo-code/src/App.css +38 -0
  36. package/dist/demo-code/src/App.js +29 -0
  37. package/dist/demo-code/src/index.js +11 -0
  38. package/dist/hooks/index.js +39 -0
  39. package/dist/hooks/useAudioVideoPermission.js +96 -0
  40. package/dist/hooks/useAudioVideoPermissions.js +1 -0
  41. package/dist/hooks/useFullScreenData.js +55 -0
  42. package/dist/hooks/useFullScreenStatus.js +45 -0
  43. package/dist/hooks/usePageVisibility.js +31 -0
  44. package/dist/hooks/useTabSwitchCount.js +25 -0
  45. package/dist/hooks/useWebcamData.js +91 -0
  46. package/dist/index.js +5 -3
  47. package/dist/lib/index.js +19 -0
  48. package/dist/utils/ValidityChecks.js +1 -19
  49. package/dist/utils/arrayUtils.js +16 -0
  50. package/dist/utils/breachUtils.js +69 -0
  51. package/dist/utils/browserUtils.js +63 -0
  52. package/dist/utils/{GetBrowserDocumentProp.js → getBrowserDocumentProp.js} +0 -0
  53. package/dist/utils/getGlancePercentage.js +1 -43
  54. package/dist/utils/{GetIsDocumentHidden.js → getIsDocumentHidden.js} +2 -2
  55. package/dist/utils/index.js +49 -29
  56. package/dist/utils/localStorageUtils.js +33 -0
  57. package/dist/utils/webcamMicrophoneUtils.js +115 -0
  58. package/dist/utils/webcamUtils.js +79 -0
  59. package/package.json +55 -55
  60. package/dist/ProctorApp.js +0 -186
  61. package/dist/utils/TabVisibility.js +0 -32
  62. package/dist/utils/commonUtilConfigs.js +0 -21
  63. package/dist/utils/initialSetup.js +0 -15
  64. package/dist/utils/localStorageUtil.js +0 -27
  65. package/dist/utils/useFullScreenStatus.js +0 -45
package/README.md CHANGED
@@ -1,66 +1,70 @@
1
- # Welcome to react_proctoring_library · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Newton-School/proctoring_react/blob/main/LICENSE) [![npm (scoped)](https://img.shields.io/npm/v/proctoring_react.svg)](https://www.npmjs.com/package/react_proctoring_library) [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/proctoring_react.svg)](https://www.npmjs.com/package/react_proctoring_library) [![CircleCI Status](https://circleci.com/gh/facebook/react.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/facebook/react) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
2
-
3
- Hi! **react_proctoring_library** is a user friendly, easy to use and your _one-stop_ [npm library](https://www.npmjs.com/package/react_proctoring_library) which will provide you the ability to proctor all your exams, tests, quizzes, Playgrounds, almost everything you need to proctor with the minimum effort possible. We have used [body-pix](https://www.npmjs.com/package/@tensorflow-models/body-pix) model from [tensorflow](https://www.tensorflow.org/) library to detect if user is looking outside the screen, multiple people visible in camera. We have also implemented tab switching tracker and full screen exit tracker.
4
-
5
- ## Why?
6
-
7
- - In these times when most of the exams are conducted online, a lot of us want some sort of mechanism which could make their life easy while taking online test and **react_proctoring_library** has done this for you.
8
-
9
- Future Plans?
10
-
11
- - Detection of multiple persons via Audio.
12
-
13
-
14
- ## Installation
15
-
16
- You must have [React](https://reactjs.org/) v16.8 or above installed in your project to use this proctoring library.
17
-
18
- If you want, you can also [create a new react app](https://reactjs.org/docs/create-a-new-react-app.html) using `npx create-react-app my-new-app` command.
19
-
20
- Once you have a react app, just type
21
- `npm install react_proctoring_library` in your terminal and if everything goes fine, you are good to go.
22
-
23
- ## Props
24
-
25
- | Props | isRequired | ParamsType | Description |
26
- | ------------------- | ---------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
27
- | `TestComponent` | Yes | JSX Component | The Component which you want to proctor |
28
- | `testIdentifier` | Yes | String | A unique string to identify every test |
29
- | `fullScreenMessage` | Optional | String | If you want your test to run only in full screen mode, this message will appear instead of TestComponent whenever User exits Full Screen |
30
-
31
- ## Documentation
32
-
33
- Currently, **react_proctoring_library** comes with 2 things:
34
-
35
- - `ProctorApp` - A Component which will help you in proctoring
36
- - `Object getStatistics(string: testIdentifier)` - A function which will return a data Object with all Statistics
37
- about breaches.
38
-
39
- ```
40
- import { ProctorApp, getStatistics } from 'react_proctoring_library';
41
- function Test(props) {
42
- return (
43
- <div>
44
- <h1>Proctoring Window</h1>
45
- </div>
46
- );
47
- }
48
-
49
- function App() {
50
- const testIdentifier = 'unique-proctoring-identifier';
51
- const fullScreenMessage = 'This test can only be completed in Full Screen Mode, do you want to start this test?';
52
- const getStats = e => {
53
- e.preventDefault();
54
- console.log(getStatistics(testIdentifier));
55
- };
56
- return (
57
- <div className="App">
58
- <ProctorApp TestComponent={Test} testIdentifier={testIdentifier} fullScreenMessage={fullScreenMessage} />
59
- </div>
60
- <button onClick={getStats}>Get Statistics</button>
61
- );
62
- }
63
-
64
- export default App;
65
- ```
66
-
1
+ # Welcome to react_proctoring_library · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Newton-School/proctoring_react/blob/main/LICENSE) [![npm (scoped)](https://img.shields.io/npm/v/proctoring_react.svg)](https://www.npmjs.com/package/react_proctoring_library) [![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/proctoring_react.svg)](https://www.npmjs.com/package/react_proctoring_library) [![CircleCI Status](https://circleci.com/gh/facebook/react.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/facebook/react) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
2
+
3
+ Hi! **react_proctoring_library** is a user friendly, easy to use and your _one-stop_ [npm library](https://www.npmjs.com/package/react_proctoring_library) which will provide you the ability to proctor all your exams, tests, quizzes, playgrounds, almost everything you need to proctor with the minimum effort possible. We have used [body-pix](https://www.npmjs.com/package/@tensorflow-models/body-pix) model from [tensorflow](https://www.tensorflow.org/) library to detect if user is looking outside the screen, multiple people visible in camera. We have also implemented tab switching tracker and full screen exit tracker.
4
+
5
+ ## Why?
6
+
7
+ - In these times when most of the exams are conducted online, a lot of us want some sort of mechanism which could make their life easy while taking online test and **react_proctoring_library** has done this for you.
8
+
9
+ Future Plans?
10
+
11
+ - Detection of multiple persons via Audio.
12
+ - Provision of videos of the proctored sessios.
13
+ - Screenshots when proctoring params are breached.
14
+
15
+ ## Installation
16
+
17
+ You must have [React](https://reactjs.org/) v16.8 or above installed in your project to use this proctoring library.
18
+
19
+ If you want, you can also [create a new react app](https://reactjs.org/docs/create-a-new-react-app.html) using `npx create-react-app my-new-app` command.
20
+
21
+ Once you have a react app, just type
22
+ `npm install react_proctoring_library` in your terminal and if everything goes fine, you are good to go.
23
+
24
+ ## Props
25
+
26
+ | Props | isRequired | ParamsType | Description |
27
+ | ---------------------- | ---------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | `children` | Yes | JSX Component | The Component which you want to proctor |
29
+ | `proctoringIdentifier` | Yes | String | A unique string to identify every test |
30
+ | `proctoringParams` | Optional | Object {userCount tabSwitch, fullscreenExit, lookedAway} | Parameters on which the screen is proctored. By default, all parameters are true |
31
+ | `shouldSendDataOnBreach` | Optional | Boolean | True if data needs to be sent on breach, else False. Default is False |
32
+ | `sendData` | Optional | Function | if shouldSendDataOnBreach is True, this callback is called with the details of the breach on every breach |
33
+
34
+ ## Documentation
35
+
36
+ Currently, **react_proctoring_library** comes with 2 things:
37
+
38
+ - `ProctorApp` - A Component which will help you in proctoring
39
+ - `Object getStatistics(string: proctoringIdentifier)` - A function which will return a data Object with all Statistics
40
+ about breaches.
41
+
42
+ ```
43
+ import { ProctorApp, getStatistics } from 'react_proctoring_library';
44
+ function Test(props) {
45
+ return (
46
+ <div>
47
+ <h1>Proctoring Window</h1>
48
+ </div>
49
+ );
50
+ }
51
+
52
+ function App() {
53
+ const proctoringIdentifier = 'unique-proctoring-identifier';
54
+
55
+ const getStats = e => {
56
+ e.preventDefault();
57
+ console.log(getStatistics(proctoringIdentifier));
58
+ };
59
+ return (
60
+ <div className="App">
61
+ <ProctorApp proctoringIdentifier={proctoringIdentifier}>
62
+ <Test />
63
+ </ProctorApp>
64
+ </div>
65
+ <button onClick={getStats}>Get Statistics</button>
66
+ );
67
+ }
68
+
69
+ export default App;
70
+ ```
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = FullScreenPermission;
7
+
8
+ require("core-js/modules/web.dom-collections.iterator.js");
9
+
10
+ require("./full-screen-permission.scss");
11
+
12
+ var _react = _interopRequireWildcard(require("react"));
13
+
14
+ var _reactBootstrap = require("react-bootstrap");
15
+
16
+ var text = _interopRequireWildcard(require("../constants/text"));
17
+
18
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
19
+
20
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
21
+
22
+ function FullScreenPermission(_ref) {
23
+ let {
24
+ firstTimeFullScreen,
25
+ setFirstTimeFullScreen,
26
+ isFullscreen,
27
+ setFullscreen,
28
+ setFullScreenExitCount,
29
+ fullScreenPermissionTexts
30
+ } = _ref;
31
+ const [show, setShow] = (0, _react.useState)(true);
32
+
33
+ const closePermissionModal = () => {
34
+ if (firstTimeFullScreen) {
35
+ setFirstTimeFullScreen(false);
36
+ }
37
+
38
+ setShow(false);
39
+ };
40
+
41
+ const handleNo = () => {
42
+ if (firstTimeFullScreen) {
43
+ setFullScreenExitCount(fullScreenExitCount => fullScreenExitCount + 1);
44
+ }
45
+
46
+ closePermissionModal();
47
+ };
48
+
49
+ const switchToFullscreenMode = () => {
50
+ closePermissionModal();
51
+ setFullscreen();
52
+ };
53
+
54
+ (0, _react.useEffect)(() => {
55
+ if (!isFullscreen) {
56
+ setShow(true);
57
+ }
58
+ }, [isFullscreen]);
59
+ const {
60
+ title = "",
61
+ message = "",
62
+ deny = "",
63
+ allow = "",
64
+ errorMessage = ""
65
+ } = fullScreenPermissionTexts;
66
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("h1", {
67
+ className: "error-message"
68
+ }, errorMessage ? errorMessage : text.FULLSCREEN_ERROR_MESSAGE), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal, {
69
+ show: show,
70
+ onHide: closePermissionModal,
71
+ size: "lg",
72
+ backdrop: "static",
73
+ "aria-labelledby": "contained-modal-title-vcenter",
74
+ centered: true
75
+ }, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Header, null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Title, null, title ? title : text.FULLSCREEN_PERMISSION_TITLE)), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Body, {
76
+ className: "dialog-message"
77
+ }, message ? message : text.MESSAGE), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Footer, null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Button, {
78
+ variant: "secondary",
79
+ onClick: handleNo
80
+ }, deny ? deny : text.DENY), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Button, {
81
+ variant: "primary",
82
+ onClick: switchToFullscreenMode
83
+ }, allow ? allow : text.ALLOW))));
84
+ }
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ require("core-js/modules/web.dom-collections.iterator.js");
4
+
3
5
  Object.defineProperty(exports, "__esModule", {
4
6
  value: true
5
7
  });
@@ -11,11 +13,21 @@ var _reactWebcam = _interopRequireDefault(require("react-webcam"));
11
13
 
12
14
  var _reactBootstrap = require("react-bootstrap");
13
15
 
16
+ var text = _interopRequireWildcard(require("../constants/text"));
17
+
18
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
19
+
20
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
21
+
14
22
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
23
 
16
- const FullScreenTestInWebcam = props => {
17
- const webcamReference = props.webcamRef,
18
- canvasReference = props.canvasRef;
24
+ const FullScreenTestInWebcam = _ref => {
25
+ let {
26
+ webcamReference,
27
+ canvasReference,
28
+ userCount,
29
+ isWatching
30
+ } = _ref;
19
31
  return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Col, {
20
32
  md: 6
21
33
  }, /*#__PURE__*/_react.default.createElement(_reactWebcam.default, {
@@ -29,20 +41,20 @@ const FullScreenTestInWebcam = props => {
29
41
  }, /*#__PURE__*/_react.default.createElement("canvas", {
30
42
  id: "glanceTrackerCanvas",
31
43
  style: {
32
- display: 'none'
44
+ display: "none"
33
45
  }
34
46
  })), /*#__PURE__*/_react.default.createElement("div", {
35
47
  style: {
36
- position: 'absolute',
37
- marginLeft: 'auto',
38
- marginRight: 'auto',
48
+ position: "absolute",
49
+ marginLeft: "auto",
50
+ marginRight: "auto",
39
51
  left: 0,
40
52
  right: 0,
41
- top: '35rem',
42
- textAlign: 'center',
53
+ top: "35rem",
54
+ textAlign: "center",
43
55
  zindex: 9
44
56
  }
45
- }, "user count: ", props.userCount, /*#__PURE__*/_react.default.createElement("br", null), props.isWatching ? 'You are watching the screen' : 'You are not watching'));
57
+ }, text.USER_COUNT_LABEL, ": ", userCount, /*#__PURE__*/_react.default.createElement("br", null), isWatching ? text.WATCHING_SCREEN : text.NOT_WATCHING_SCREEN));
46
58
  };
47
59
 
48
60
  var _default = FullScreenTestInWebcam;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.ProctorApp = void 0;
7
+
8
+ require("core-js/modules/web.dom-collections.iterator.js");
9
+
10
+ var _react = _interopRequireWildcard(require("react"));
11
+
12
+ var _ = require(".");
13
+
14
+ var _utils = require("../utils");
15
+
16
+ var _hooks = require("../hooks");
17
+
18
+ var _defaults = require("../constants/defaults");
19
+
20
+ var _permissions = require("./permissions");
21
+
22
+ var _useWebcamData = _interopRequireDefault(require("../hooks/useWebcamData"));
23
+
24
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
+
26
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
27
+
28
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
29
+
30
+ const ProctorApp = _ref => {
31
+ let {
32
+ proctoringIdentifier,
33
+ children,
34
+ proctoringParams = {
35
+ userCount: true,
36
+ tabSwitch: true,
37
+ fullscreenExit: true,
38
+ lookedAway: true
39
+ },
40
+ sendData = () => {},
41
+ shouldSendDataOnBreach = false
42
+ } = _ref;
43
+ const webcamReference = (0, _react.useRef)(null);
44
+ const canvasReference = (0, _react.useRef)(null);
45
+ const statistics = (0, _react.useRef)(_defaults.INITIAL_STATISTICS);
46
+ const [audioPermission, setAudioPermission] = (0, _react.useState)(false);
47
+ const [videoPermission, setVideoPermission] = (0, _react.useState)(false);
48
+ const {
49
+ isFullscreen,
50
+ setFullscreen,
51
+ fullScreenExitCount
52
+ } = (0, _hooks.useFullscreenData)();
53
+ const [userCount, lookedAwayCount] = (0, _useWebcamData.default)(webcamReference, canvasReference);
54
+ const tabSwitchCount = (0, _hooks.useTabSwitchCount)();
55
+ (0, _react.useEffect)(() => {
56
+ (0, _utils.removeStatsFromLocalStorage)(proctoringIdentifier);
57
+ }, [proctoringIdentifier]);
58
+ (0, _react.useEffect)(() => {
59
+ if (shouldSendDataOnBreach) {
60
+ (0, _utils.sendDataOnBreach)(statistics, {
61
+ userCount,
62
+ tabSwitchCount,
63
+ fullScreenExitCount,
64
+ lookedAwayCount
65
+ }, proctoringIdentifier, sendData);
66
+ }
67
+
68
+ (0, _utils.updateStatistics)(statistics, tabSwitchCount, fullScreenExitCount, lookedAwayCount, userCount);
69
+ (0, _utils.addOrUpdateStatsToLocalStorage)(proctoringIdentifier, statistics.current);
70
+ }, [userCount, tabSwitchCount, fullScreenExitCount, lookedAwayCount, proctoringIdentifier, sendData, shouldSendDataOnBreach]);
71
+
72
+ if (proctoringIdentifier === undefined) {
73
+ // todo
74
+ return;
75
+ }
76
+
77
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_permissions.Permission, {
78
+ isFullscreen: isFullscreen,
79
+ audioPermisison: audioPermission,
80
+ videoPermission: videoPermission,
81
+ setFullscreen: setFullscreen
82
+ }), /*#__PURE__*/_react.default.createElement(_.ActivityTracker, {
83
+ setAudioPermission: setAudioPermission,
84
+ setVideoPermission: setVideoPermission,
85
+ webcamReference: webcamReference,
86
+ canvasReference: canvasReference
87
+ }), isFullscreen && audioPermission && videoPermission && children);
88
+ };
89
+
90
+ exports.ProctorApp = ProctorApp;
91
+ var _default = ProctorApp;
92
+ exports.default = _default;
@@ -11,26 +11,34 @@ var _react = _interopRequireWildcard(require("react"));
11
11
 
12
12
  var _reactBootstrap = require("react-bootstrap");
13
13
 
14
+ var text = _interopRequireWildcard(require("../constants/text"));
15
+
14
16
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
17
 
16
18
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
19
 
18
- function ResponseModal(props) {
20
+ function ResponseModal(_ref) {
21
+ let {
22
+ firstTimeFullScreen,
23
+ setFirstTimeFullScreen,
24
+ isFullscreen,
25
+ setFullscreen,
26
+ setFullScreenExitCount,
27
+ fullScreenMessage
28
+ } = _ref;
19
29
  const [show, setShow] = (0, _react.useState)(true);
20
30
 
21
- const displayResponseModal = () => setShow(true);
22
-
23
31
  const closeResponseModal = () => {
24
- if (props.firstTimeFullScreen) {
25
- props.setFirstTimeFullScreen(false);
32
+ if (firstTimeFullScreen) {
33
+ setFirstTimeFullScreen(false);
26
34
  }
27
35
 
28
36
  setShow(false);
29
37
  };
30
38
 
31
- const handleNoOption = () => {
32
- if (props.firstTimeFullScreen) {
33
- props.setFullScreenExitCount(fullScreenExitCount => fullScreenExitCount + 1);
39
+ const handleNo = () => {
40
+ if (firstTimeFullScreen) {
41
+ setFullScreenExitCount(fullScreenExitCount => fullScreenExitCount + 1);
34
42
  }
35
43
 
36
44
  closeResponseModal();
@@ -38,14 +46,14 @@ function ResponseModal(props) {
38
46
 
39
47
  const openFullscreen = () => {
40
48
  closeResponseModal();
41
- props.SetCanOpenFullScreen();
49
+ setFullscreen();
42
50
  };
43
51
 
44
52
  (0, _react.useEffect)(() => {
45
- if (!props.canOpenFullScreen) {
46
- displayResponseModal();
53
+ if (!isFullscreen) {
54
+ setShow(true);
47
55
  }
48
- }, [props.canOpenFullScreen]);
56
+ }, [isFullscreen]);
49
57
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal, {
50
58
  show: show,
51
59
  onHide: closeResponseModal,
@@ -53,11 +61,11 @@ function ResponseModal(props) {
53
61
  backdrop: "static",
54
62
  "aria-labelledby": "contained-modal-title-vcenter",
55
63
  centered: true
56
- }, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Header, null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Title, null, "Full Screen Permission")), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Body, null, props.fullScreenMessage), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Footer, null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Button, {
64
+ }, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Header, null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Title, null, text.FULLSCREEN_PERMISSION_TITLE)), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Body, null, fullScreenMessage), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Modal.Footer, null, /*#__PURE__*/_react.default.createElement(_reactBootstrap.Button, {
57
65
  variant: "secondary",
58
- onClick: handleNoOption
59
- }, "No"), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Button, {
66
+ onClick: handleNo
67
+ }, text.NO), /*#__PURE__*/_react.default.createElement(_reactBootstrap.Button, {
60
68
  variant: "primary",
61
69
  onClick: openFullscreen
62
- }, "Yes"))));
70
+ }, text.YES))));
63
71
  }
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+
3
+ require("core-js/modules/web.dom-collections.iterator.js");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = void 0;
9
+
10
+ require("core-js/modules/es.regexp.exec.js");
11
+
12
+ require("core-js/modules/es.string.match.js");
13
+
14
+ var _react = _interopRequireWildcard(require("react"));
15
+
16
+ var _reactWebcam = _interopRequireDefault(require("react-webcam"));
17
+
18
+ var _utils = require("../../utils");
19
+
20
+ var _webcamMicrophoneUtils = require("../../utils/webcamMicrophoneUtils");
21
+
22
+ require("./index.scss");
23
+
24
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
+
26
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
27
+
28
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
29
+
30
+ const isChrome = !!navigator.userAgent.match(/chrome|chromium/i);
31
+
32
+ const ActivityTracker = _ref => {
33
+ let {
34
+ webcamReference,
35
+ canvasReference,
36
+ setAudioPermission,
37
+ setVideoPermission
38
+ } = _ref;
39
+ (0, _react.useEffect)(() => {
40
+ if (isChrome) {
41
+ (0, _webcamMicrophoneUtils.updateVideoPermissions)(setVideoPermission);
42
+ (0, _webcamMicrophoneUtils.updateAudioPermissions)(setAudioPermission);
43
+ (0, _webcamMicrophoneUtils.getVideoPermissionQuery)().then(status => {
44
+ status.onchange = evt => {
45
+ (0, _webcamMicrophoneUtils.updateVideoPermissions)(setVideoPermission);
46
+ };
47
+ }).catch(err => {
48
+ console.log("Video Permission", err);
49
+ });
50
+ (0, _webcamMicrophoneUtils.getAudioPermissionQuery)().then(status => {
51
+ status.onchange = evt => {
52
+ (0, _webcamMicrophoneUtils.updateAudioPermissions)(setAudioPermission);
53
+ };
54
+ }).catch(err => {
55
+ (0, _webcamMicrophoneUtils.updateAudioPermissions)("Audio Permission", err);
56
+ });
57
+ }
58
+ }, [setVideoPermission, setAudioPermission]);
59
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactWebcam.default, {
60
+ audio: true,
61
+ muted: true,
62
+ ref: webcamReference,
63
+ onUserMedia: stream => {
64
+ if (!isChrome) {
65
+ const {
66
+ audio,
67
+ video
68
+ } = (0, _utils.getAudioVideoPermission)(stream);
69
+ setAudioPermission(audio);
70
+ setVideoPermission(video);
71
+
72
+ stream.onaddtrack = p => {
73
+ const {
74
+ audio,
75
+ video
76
+ } = (0, _utils.getAudioVideoPermission)(stream);
77
+ setAudioPermission(audio);
78
+ setVideoPermission(video);
79
+ };
80
+
81
+ stream.onremovetrack = p => {
82
+ const {
83
+ audio,
84
+ video
85
+ } = (0, _utils.getAudioVideoPermission)(stream);
86
+ setAudioPermission(audio);
87
+ setVideoPermission(video);
88
+ };
89
+
90
+ stream.onactive = p => {
91
+ const {
92
+ audio,
93
+ video
94
+ } = (0, _utils.getAudioVideoPermission)(stream);
95
+ setAudioPermission(audio);
96
+ setVideoPermission(video);
97
+ };
98
+
99
+ stream.oninactive = e => {
100
+ setAudioPermission(false);
101
+ setVideoPermission(false);
102
+ };
103
+
104
+ const audioTracks = stream.getAudioTracks().filter(track => track.enabled && track.label);
105
+ audioTracks.forEach(track => {
106
+ track.onended = e => {
107
+ const audio = (0, _utils.getAudioPermission)(stream);
108
+ setAudioPermission(audio, e.currentTarget.id);
109
+ };
110
+
111
+ track.onmute = e => {
112
+ const audio = (0, _utils.getAudioPermission)(stream);
113
+ setAudioPermission(audio, e.currentTarget.id);
114
+ };
115
+
116
+ track.onunmute = e => {
117
+ const audio = (0, _utils.getAudioPermission)(stream);
118
+ setAudioPermission(audio);
119
+ };
120
+ });
121
+ const videoTracks = stream.getVideoTracks().filter(track => track.enabled && track.label);
122
+ videoTracks.forEach(track => {
123
+ track.onended = e => {
124
+ const video = (0, _utils.getVideoPermission)(stream, e.currentTarget.id);
125
+ setVideoPermission(video);
126
+ };
127
+ });
128
+ }
129
+ },
130
+ onUserMediaError: error => {
131
+ if (!isChrome) {
132
+ setVideoPermission(false);
133
+ setAudioPermission(false);
134
+ }
135
+ },
136
+ className: "captured-video-canvas"
137
+ }), /*#__PURE__*/_react.default.createElement("canvas", {
138
+ ref: canvasReference,
139
+ className: "captured-video-canvas"
140
+ }));
141
+ };
142
+
143
+ var _default = ActivityTracker;
144
+ exports.default = _default;
@@ -0,0 +1,4 @@
1
+ .captured-video-canvas {
2
+ visibility: hidden;
3
+ position: absolute;
4
+ }
@@ -0,0 +1,8 @@
1
+ .error-message {
2
+ display: flex;
3
+ justify-content: center;
4
+ font-size: large;
5
+ }
6
+ .dialog-message {
7
+ font-size: medium;
8
+ }