@instructure/quiz-core 20.17.3-rc.8 → 20.18.0
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.
- package/CHANGELOG.md +20 -0
- package/es/common/actions/taking.js +9 -1
- package/es/common/middleware/quizTimerWebSocketMiddleware.js +91 -0
- package/es/common/reducers/taking.js +8 -1
- package/es/common/util/TimerService.js +6 -1
- package/es/configureStore.js +4 -2
- package/es/index.js +8 -0
- package/es/taking/api/taking.js +18 -2
- package/lib/common/actions/taking.js +12 -1
- package/lib/common/middleware/quizTimerWebSocketMiddleware.js +121 -0
- package/lib/common/reducers/taking.js +7 -0
- package/lib/common/util/TimerService.js +6 -1
- package/lib/configureStore.js +4 -1
- package/lib/taking/api/taking.js +20 -0
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [20.18.0](https://gerrit.instructure.com/quizzes-ui/compare/v20.17.2...v20.18.0) (2024-09-26)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **quizzes-ui:** Add data-automation tags for impact integration ([9b710b8](https://gerrit.instructure.com/quizzes-ui/commits/9b710b87929cc2d203de0f9f68e75a5e2b1a3150))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* update remaining time in quiz session ([3a1a914](https://gerrit.instructure.com/quizzes-ui/commits/3a1a91424ba174b7f2a39cf2a8a4d669dae120ab))
|
|
17
|
+
* **quizzes-ui:** add 2-minute alert to timed quiz ([6bb83e9](https://gerrit.instructure.com/quizzes-ui/commits/6bb83e953ae989bc88975922051044bef9e4d7ce))
|
|
18
|
+
* **quizzes-ui:** add page_blurred/page_focused events ([2c6b692](https://gerrit.instructure.com/quizzes-ui/commits/2c6b6928b7a2f7926ab1757884ee63bcff6c4c37))
|
|
19
|
+
* add Cancel and Close button to ModerateTray ([e41ad88](https://gerrit.instructure.com/quizzes-ui/commits/e41ad88aaef2af576f53ff0e212c6d793db74bf4))
|
|
20
|
+
* refactor ingress for AccommodationModal and ModerateTray ([7c49355](https://gerrit.instructure.com/quizzes-ui/commits/7c49355a8b03cd20dabf480c2ba6c8c616a7eb65))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [20.17.2](https://gerrit.instructure.com/quizzes-ui/compare/v20.17.1...v20.17.2) (2024-09-05)
|
|
7
27
|
|
|
8
28
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuid } from 'uuid';
|
|
2
|
-
import { BACKTRACKING_NEXT_QUESTION, CLEAR_FOCUS_CURRENT_SESSION_ITEM, LOAD_RESUME_DATA, PREVIEW_RESPONSE, PREVIOUS_QUESTION, QUIZ_SUBMISSION_EVENT, UPDATE_QUIZ_SUBMIT_FLAG, RESPONSE, SESSION_STARTED_OR_RESUMED_EVENT, SET_CURRENT_SESSION_ITEM_POSITION, SET_FOCUS_TITLE_AND_INSTRUCTIONS, UPDATE_IS_TAKING, UPDATE_QUIZ_SESSION_STATUS_ON_PAGE_LOAD, UPDATE_TIMER_MINIMIZED, UPDATE_TIMER_INFO, PAGE_BLURRED_EVENT, PAGE_FOCUSED_EVENT } from '@instructure/quiz-common';
|
|
2
|
+
import { BACKTRACKING_NEXT_QUESTION, CLEAR_FOCUS_CURRENT_SESSION_ITEM, LOAD_RESUME_DATA, PREVIEW_RESPONSE, PREVIOUS_QUESTION, QUIZ_SUBMISSION_EVENT, UPDATE_QUIZ_SUBMIT_FLAG, RESPONSE, SESSION_STARTED_OR_RESUMED_EVENT, SET_CURRENT_SESSION_ITEM_POSITION, SET_FOCUS_TITLE_AND_INSTRUCTIONS, UPDATE_IS_TAKING, UPDATE_QUIZ_SESSION_STATUS_ON_PAGE_LOAD, UPDATE_TIMER_MINIMIZED, UPDATE_TIMER_INFO, SET_WS_TOKEN, PAGE_BLURRED_EVENT, PAGE_FOCUSED_EVENT } from '@instructure/quiz-common';
|
|
3
3
|
export var setCurrentSessionItemPosition = function setCurrentSessionItemPosition(position) {
|
|
4
4
|
return {
|
|
5
5
|
type: SET_CURRENT_SESSION_ITEM_POSITION,
|
|
@@ -135,6 +135,14 @@ export var submitQuizSessionEvent = function submitQuizSessionEvent() {
|
|
|
135
135
|
payload: {}
|
|
136
136
|
};
|
|
137
137
|
};
|
|
138
|
+
export var setWSToken = function setWSToken(wsToken) {
|
|
139
|
+
return {
|
|
140
|
+
type: SET_WS_TOKEN,
|
|
141
|
+
payload: {
|
|
142
|
+
wsToken: wsToken
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
};
|
|
138
146
|
export var updateQuizSubmitFlag = function updateQuizSubmitFlag(value) {
|
|
139
147
|
return {
|
|
140
148
|
type: UPDATE_QUIZ_SUBMIT_FLAG,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { SET_WS_TOKEN, QUIZ_SUBMISSION_EVENT } from '@instructure/quiz-common';
|
|
2
|
+
import { featureOn } from "../../common/util/featureCheck.js";
|
|
3
|
+
import { formatTime } from "../../common/util/formatTimespan.js";
|
|
4
|
+
import { updateTime, notifyTimeAdded } from "../../taking/api/taking.js";
|
|
5
|
+
import { bindActionCreators } from 'redux';
|
|
6
|
+
var takingApiActions = {
|
|
7
|
+
updateTime: updateTime,
|
|
8
|
+
notifyTimeAdded: notifyTimeAdded
|
|
9
|
+
};
|
|
10
|
+
export var createWebsocket = function createWebsocket(url, token) {
|
|
11
|
+
return new WebSocket("".concat(url, "?token=").concat(token));
|
|
12
|
+
};
|
|
13
|
+
export var handleExtraTime = function handleExtraTime(extraTimeInSeconds, endAt, updateTimeAction, notifyTimeAdded) {
|
|
14
|
+
var remainingTimeInMs = new Date(endAt) - Date.now();
|
|
15
|
+
var remainingTimeInSeconds = Math.floor(remainingTimeInMs / 1000);
|
|
16
|
+
var formattedExtraTimeText = formatTime(extraTimeInSeconds);
|
|
17
|
+
updateTimeAction(remainingTimeInSeconds);
|
|
18
|
+
notifyTimeAdded(formattedExtraTimeText);
|
|
19
|
+
};
|
|
20
|
+
export var parseJwt = function parseJwt(token) {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
|
23
|
+
} catch (e) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export var initializeWebSocket = function initializeWebSocket(token, createWebsocketFn) {
|
|
28
|
+
var jwt = parseJwt(token);
|
|
29
|
+
|
|
30
|
+
if (!jwt) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return createWebsocketFn(jwt['aud'], token);
|
|
35
|
+
};
|
|
36
|
+
export var handleWebSocketMessage = function handleWebSocketMessage(webSocketMessage, boundTakingApiActions, handleExtraTimeFn) {
|
|
37
|
+
var _webSocketMessageData, _webSocketMessageData2, _webSocketMessageData3;
|
|
38
|
+
|
|
39
|
+
var webSocketMessageData = JSON.parse(webSocketMessage.data);
|
|
40
|
+
webSocketMessageData = ((_webSocketMessageData = webSocketMessageData) === null || _webSocketMessageData === void 0 ? void 0 : (_webSocketMessageData2 = _webSocketMessageData.message) === null || _webSocketMessageData2 === void 0 ? void 0 : _webSocketMessageData2.data) || ((_webSocketMessageData3 = webSocketMessageData) === null || _webSocketMessageData3 === void 0 ? void 0 : _webSocketMessageData3.data);
|
|
41
|
+
|
|
42
|
+
if (!webSocketMessageData) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var extra_time_in_seconds = webSocketMessageData.extra_time_in_seconds;
|
|
47
|
+
var end_at = webSocketMessageData.end_at;
|
|
48
|
+
|
|
49
|
+
if (!extra_time_in_seconds || !end_at) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
handleExtraTimeFn(extra_time_in_seconds, end_at, boundTakingApiActions.updateTime, boundTakingApiActions.notifyTimeAdded);
|
|
54
|
+
};
|
|
55
|
+
export var createQuizTimerWebSocketMiddleware = function createQuizTimerWebSocketMiddleware() {
|
|
56
|
+
var createWebsocketFn = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : createWebsocket;
|
|
57
|
+
var featureOnFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : featureOn;
|
|
58
|
+
var socket = null;
|
|
59
|
+
return function (store) {
|
|
60
|
+
return function (next) {
|
|
61
|
+
return function (action) {
|
|
62
|
+
var addTimeToExistingQuizSessionsEnabled = featureOnFn('add_time_to_existing_quiz_sessions');
|
|
63
|
+
|
|
64
|
+
if (!addTimeToExistingQuizSessionsEnabled) {
|
|
65
|
+
return next(action);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (action.type === SET_WS_TOKEN && socket === null) {
|
|
69
|
+
var token = action.payload.wsToken;
|
|
70
|
+
socket = initializeWebSocket(token, createWebsocketFn);
|
|
71
|
+
|
|
72
|
+
if (!socket) {
|
|
73
|
+
return next(action);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var boundTakingApiActions = bindActionCreators(takingApiActions, store.dispatch);
|
|
77
|
+
socket.addEventListener('message', function (webSocketMessage) {
|
|
78
|
+
handleWebSocketMessage(webSocketMessage, boundTakingApiActions, handleExtraTime);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (action.type === QUIZ_SUBMISSION_EVENT && socket !== null) {
|
|
83
|
+
socket.close();
|
|
84
|
+
socket = null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return next(action);
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Map } from 'immutable';
|
|
2
|
-
import { ACTIVE_QUIZ_SESSION_CHANGE, UPDATE_TIMER_INFO, UPDATE_IS_TAKING, UPDATE_QUIZ_SESSION_STATUS_ON_PAGE_LOAD, UPDATE_TIMER_MINIMIZED, LOAD_RESUME_DATA, PREVIEW_RESPONSE, PREVIOUS_QUESTION, BACKTRACKING_NEXT_QUESTION, RESPONSE, SESSION_STARTED_OR_RESUMED_EVENT, SET_CURRENT_SESSION_ITEM_POSITION, CLEAR_FOCUS_CURRENT_SESSION_ITEM, SET_FOCUS_TITLE_AND_INSTRUCTIONS, UPDATE_QUIZ_SUBMIT_FLAG } from '@instructure/quiz-common';
|
|
2
|
+
import { ACTIVE_QUIZ_SESSION_CHANGE, UPDATE_TIMER_INFO, UPDATE_IS_TAKING, UPDATE_QUIZ_SESSION_STATUS_ON_PAGE_LOAD, UPDATE_TIMER_MINIMIZED, LOAD_RESUME_DATA, PREVIEW_RESPONSE, PREVIOUS_QUESTION, BACKTRACKING_NEXT_QUESTION, RESPONSE, SESSION_STARTED_OR_RESUMED_EVENT, SET_CURRENT_SESSION_ITEM_POSITION, CLEAR_FOCUS_CURRENT_SESSION_ITEM, SET_FOCUS_TITLE_AND_INSTRUCTIONS, UPDATE_QUIZ_SUBMIT_FLAG, SET_WS_TOKEN } from '@instructure/quiz-common';
|
|
3
3
|
|
|
4
4
|
var defaultResponse = function defaultResponse(payload) {
|
|
5
5
|
return Map({
|
|
@@ -53,6 +53,10 @@ var updateTimerInfo = function updateTimerInfo(state, payload) {
|
|
|
53
53
|
return state.mergeIn(['timerData'], payload);
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
var setWSToken = function setWSToken(state, payload) {
|
|
57
|
+
return state.set('wsToken', payload === null || payload === void 0 ? void 0 : payload.wsToken);
|
|
58
|
+
};
|
|
59
|
+
|
|
56
60
|
var updateTimerMinimized = function updateTimerMinimized(state, payload) {
|
|
57
61
|
return state.setIn(['timerData', 'timerMinimized'], payload.timerMinimized);
|
|
58
62
|
};
|
|
@@ -126,6 +130,9 @@ export default (function () {
|
|
|
126
130
|
case UPDATE_TIMER_INFO:
|
|
127
131
|
return updateTimerInfo(state, action.payload);
|
|
128
132
|
|
|
133
|
+
case SET_WS_TOKEN:
|
|
134
|
+
return setWSToken(state, action.payload);
|
|
135
|
+
|
|
129
136
|
case SET_CURRENT_SESSION_ITEM_POSITION:
|
|
130
137
|
return setCurrentSessionItemPosition(state, action.payload);
|
|
131
138
|
|
|
@@ -5,7 +5,7 @@ import reduxStore from "../../reduxStore.js";
|
|
|
5
5
|
export var MAX_SETTIMEOUT_IN_MILLISECONDS = 20 * 24 * 3600 * 1000; // ms of 20 days
|
|
6
6
|
|
|
7
7
|
export var TimerService = /*#__PURE__*/function () {
|
|
8
|
-
function TimerService(getTimeRemaining, updateTimeRemaining, getTimerData, submitQuiz, alert) {
|
|
8
|
+
function TimerService(getTimeRemaining, updateTimeRemaining, getTimerData, submitQuiz, alert, setWsToken) {
|
|
9
9
|
var _this = this;
|
|
10
10
|
|
|
11
11
|
_classCallCheck(this, TimerService);
|
|
@@ -23,6 +23,10 @@ export var TimerService = /*#__PURE__*/function () {
|
|
|
23
23
|
_this.__onTimerChange(data);
|
|
24
24
|
|
|
25
25
|
_this.__setApiCallIntervalTime(data.timeLeftInSeconds);
|
|
26
|
+
|
|
27
|
+
if (_this._setWsToken) {
|
|
28
|
+
_this._setWsToken(data.wsToken);
|
|
29
|
+
}
|
|
26
30
|
});
|
|
27
31
|
};
|
|
28
32
|
|
|
@@ -31,6 +35,7 @@ export var TimerService = /*#__PURE__*/function () {
|
|
|
31
35
|
this._getTimerData = getTimerData;
|
|
32
36
|
this._submitQuiz = submitQuiz;
|
|
33
37
|
this._alert = alert;
|
|
38
|
+
this._setWsToken = setWsToken;
|
|
34
39
|
this._stopped = false;
|
|
35
40
|
this._apiInterval = null;
|
|
36
41
|
this._clockInterval = null;
|
package/es/configureStore.js
CHANGED
|
@@ -9,7 +9,8 @@ import multi from 'redux-multi';
|
|
|
9
9
|
import thunkMiddleware from 'redux-thunk';
|
|
10
10
|
import alertTimerMiddleware from "./common/middleware/alertTimerMiddleware.js";
|
|
11
11
|
import crossDomainMessagingMiddleware from "./common/middleware/crossDomainMessagingMiddleware.js";
|
|
12
|
-
import eventLoggerMiddlewareCreator from "./common/middleware/eventLoggerMiddleware.js";
|
|
12
|
+
import eventLoggerMiddlewareCreator from "./common/middleware/eventLoggerMiddleware.js";
|
|
13
|
+
import { createQuizTimerWebSocketMiddleware } from "./common/middleware/quizTimerWebSocketMiddleware.js"; // reducers
|
|
13
14
|
|
|
14
15
|
import alerts from "./common/reducers/alerts.js";
|
|
15
16
|
import banks from "./common/reducers/banks.js";
|
|
@@ -69,7 +70,8 @@ export function configureStore(_ref) {
|
|
|
69
70
|
}
|
|
70
71
|
});
|
|
71
72
|
var kinesisMiddleware = eventLoggerMiddlewareCreator();
|
|
72
|
-
var
|
|
73
|
+
var websocketMiddleware = createQuizTimerWebSocketMiddleware();
|
|
74
|
+
var baseMiddleware = [alertTimerMiddleware, crossDomainMessagingMiddleware, multi, kinesisMiddleware, thunkMiddleware, websocketMiddleware];
|
|
73
75
|
var devMiddleware = [loggerMiddleware];
|
|
74
76
|
var middlewareToApply = inDevelopment ? union(baseMiddleware, devMiddleware) : baseMiddleware;
|
|
75
77
|
middlewareToApply = union(middlewareToApply, extraMiddleware);
|
package/es/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
/* eslint "import/newline-after-import": "warn" */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* In g/356542 we tried importing takingApiActions from here, and it caused a weird karma issue.
|
|
6
|
+
* Nothing from this file should be imported from within quiz-core!
|
|
7
|
+
* These exports are for other packages to use.
|
|
8
|
+
* We should look into configuring an import/no-restricted-paths eslint rule to prevent this.
|
|
9
|
+
*/
|
|
2
10
|
// Components
|
|
3
11
|
export { DragDroppableSDKApp as SDKApp } from "./common/components/SDKApp/index.js";
|
|
4
12
|
export { ModerateTable } from "./moderating/components/resources/ModerateTable/index.js";
|
package/es/taking/api/taking.js
CHANGED
|
@@ -11,7 +11,7 @@ import { Map } from 'immutable';
|
|
|
11
11
|
import { camelizeKeys } from 'humps';
|
|
12
12
|
import t from '@instructure/quiz-i18n/es/format-message';
|
|
13
13
|
import noop from "../../common/util/noop.js";
|
|
14
|
-
import { screenreaderNotification } from "../../common/actions/alerts.js";
|
|
14
|
+
import { addInfo, screenreaderNotification } from "../../common/actions/alerts.js";
|
|
15
15
|
import Item from "../../common/records/Item.js";
|
|
16
16
|
import { featureOn } from "../../common/util/featureCheck.js";
|
|
17
17
|
import { fetchLogger } from "../../common/kinesis/QuizEventsLogger.js";
|
|
@@ -21,7 +21,7 @@ import { fetchQuizSession, fetchResumeData, fetchTime, recordTime, singleRespons
|
|
|
21
21
|
import { getSessionItems, handleQuizSessionFetch } from "../../common/actions/quizSessions.js";
|
|
22
22
|
import { handleSessionItemsResponse, handleResumeDataResponse, handleQuizSessionResponse } from "../../common/api/callHandlers.js";
|
|
23
23
|
import * as modalActions from "../../common/actions/modal.js";
|
|
24
|
-
import { backtrackingNextQuestion, sessionStartOrResumeEvent, setCurrentSessionItemPosition, submitQuizSessionEvent, updateQuizSubmitFlag, updateQuizSessionStatusOnPageLoad } from "../../common/actions/taking.js";
|
|
24
|
+
import { backtrackingNextQuestion, sessionStartOrResumeEvent, setCurrentSessionItemPosition, submitQuizSessionEvent, updateQuizSubmitFlag, updateQuizSessionStatusOnPageLoad, updateTimerInfo } from "../../common/actions/taking.js";
|
|
25
25
|
|
|
26
26
|
var getQuizSessionConfig = function getQuizSessionConfig(quizSession) {
|
|
27
27
|
// these should be handled by existing functions
|
|
@@ -244,6 +244,22 @@ export function submitQuiz(quizSessionId) {
|
|
|
244
244
|
}
|
|
245
245
|
};
|
|
246
246
|
}
|
|
247
|
+
export function notifyTimeAdded(extraTime) {
|
|
248
|
+
return function (dispatch) {
|
|
249
|
+
return dispatch(addInfo(t('Your original time limit has been extended by { extraTime }', {
|
|
250
|
+
extraTime: extraTime
|
|
251
|
+
})));
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
export function updateTime(timeInSeconds) {
|
|
255
|
+
return function (dispatch) {
|
|
256
|
+
return dispatch(updateTimerInfo({
|
|
257
|
+
timeLeftInSeconds: timeInSeconds,
|
|
258
|
+
timerRunning: true,
|
|
259
|
+
timeRecording: true
|
|
260
|
+
}));
|
|
261
|
+
};
|
|
262
|
+
}
|
|
247
263
|
|
|
248
264
|
function getQuestionsNeedingAResponse(state, quizSessionId) {
|
|
249
265
|
var responses = state.getIn(['taking', 'responses'], Map());
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.updateQuizSubmitFlag = exports.submitQuizSessionEvent = exports.reportPageFocusedEvent = exports.reportPageBlurredEvent = exports.sessionStartOrResumeEvent = exports.loadResumeData = exports.editResponse = exports.previewResponse = exports.updateTimerInfo = exports.updateQuizSessionStatusOnPageLoad = exports.updateIsTaking = exports.updateTimerMinimized = exports.backtrackingNextQuestion = exports.previousQuestion = exports.setFocusTitleAndInstructions = exports.clearFocusCurrentSessionItem = exports.setCurrentSessionItemPosition = void 0;
|
|
6
|
+
exports.updateQuizSubmitFlag = exports.setWSToken = exports.submitQuizSessionEvent = exports.reportPageFocusedEvent = exports.reportPageBlurredEvent = exports.sessionStartOrResumeEvent = exports.loadResumeData = exports.editResponse = exports.previewResponse = exports.updateTimerInfo = exports.updateQuizSessionStatusOnPageLoad = exports.updateIsTaking = exports.updateTimerMinimized = exports.backtrackingNextQuestion = exports.previousQuestion = exports.setFocusTitleAndInstructions = exports.clearFocusCurrentSessionItem = exports.setCurrentSessionItemPosition = void 0;
|
|
7
7
|
|
|
8
8
|
var _uuid = require("uuid");
|
|
9
9
|
|
|
@@ -192,6 +192,17 @@ var submitQuizSessionEvent = function submitQuizSessionEvent() {
|
|
|
192
192
|
|
|
193
193
|
exports.submitQuizSessionEvent = submitQuizSessionEvent;
|
|
194
194
|
|
|
195
|
+
var setWSToken = function setWSToken(wsToken) {
|
|
196
|
+
return {
|
|
197
|
+
type: _quizCommon.SET_WS_TOKEN,
|
|
198
|
+
payload: {
|
|
199
|
+
wsToken: wsToken
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
exports.setWSToken = setWSToken;
|
|
205
|
+
|
|
195
206
|
var updateQuizSubmitFlag = function updateQuizSubmitFlag(value) {
|
|
196
207
|
return {
|
|
197
208
|
type: _quizCommon.UPDATE_QUIZ_SUBMIT_FLAG,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createQuizTimerWebSocketMiddleware = exports.handleWebSocketMessage = exports.initializeWebSocket = exports.parseJwt = exports.handleExtraTime = exports.createWebsocket = void 0;
|
|
7
|
+
|
|
8
|
+
var _quizCommon = require("@instructure/quiz-common");
|
|
9
|
+
|
|
10
|
+
var _featureCheck = require("../../common/util/featureCheck.js");
|
|
11
|
+
|
|
12
|
+
var _formatTimespan = require("../../common/util/formatTimespan.js");
|
|
13
|
+
|
|
14
|
+
var _taking = require("../../taking/api/taking.js");
|
|
15
|
+
|
|
16
|
+
var _redux = require("redux");
|
|
17
|
+
|
|
18
|
+
var takingApiActions = {
|
|
19
|
+
updateTime: _taking.updateTime,
|
|
20
|
+
notifyTimeAdded: _taking.notifyTimeAdded
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
var createWebsocket = function createWebsocket(url, token) {
|
|
24
|
+
return new WebSocket("".concat(url, "?token=").concat(token));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
exports.createWebsocket = createWebsocket;
|
|
28
|
+
|
|
29
|
+
var handleExtraTime = function handleExtraTime(extraTimeInSeconds, endAt, updateTimeAction, notifyTimeAdded) {
|
|
30
|
+
var remainingTimeInMs = new Date(endAt) - Date.now();
|
|
31
|
+
var remainingTimeInSeconds = Math.floor(remainingTimeInMs / 1000);
|
|
32
|
+
var formattedExtraTimeText = (0, _formatTimespan.formatTime)(extraTimeInSeconds);
|
|
33
|
+
updateTimeAction(remainingTimeInSeconds);
|
|
34
|
+
notifyTimeAdded(formattedExtraTimeText);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
exports.handleExtraTime = handleExtraTime;
|
|
38
|
+
|
|
39
|
+
var parseJwt = function parseJwt(token) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
exports.parseJwt = parseJwt;
|
|
48
|
+
|
|
49
|
+
var initializeWebSocket = function initializeWebSocket(token, createWebsocketFn) {
|
|
50
|
+
var jwt = parseJwt(token);
|
|
51
|
+
|
|
52
|
+
if (!jwt) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return createWebsocketFn(jwt['aud'], token);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
exports.initializeWebSocket = initializeWebSocket;
|
|
60
|
+
|
|
61
|
+
var handleWebSocketMessage = function handleWebSocketMessage(webSocketMessage, boundTakingApiActions, handleExtraTimeFn) {
|
|
62
|
+
var _webSocketMessageData, _webSocketMessageData2, _webSocketMessageData3;
|
|
63
|
+
|
|
64
|
+
var webSocketMessageData = JSON.parse(webSocketMessage.data);
|
|
65
|
+
webSocketMessageData = ((_webSocketMessageData = webSocketMessageData) === null || _webSocketMessageData === void 0 ? void 0 : (_webSocketMessageData2 = _webSocketMessageData.message) === null || _webSocketMessageData2 === void 0 ? void 0 : _webSocketMessageData2.data) || ((_webSocketMessageData3 = webSocketMessageData) === null || _webSocketMessageData3 === void 0 ? void 0 : _webSocketMessageData3.data);
|
|
66
|
+
|
|
67
|
+
if (!webSocketMessageData) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
var extra_time_in_seconds = webSocketMessageData.extra_time_in_seconds;
|
|
72
|
+
var end_at = webSocketMessageData.end_at;
|
|
73
|
+
|
|
74
|
+
if (!extra_time_in_seconds || !end_at) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
handleExtraTimeFn(extra_time_in_seconds, end_at, boundTakingApiActions.updateTime, boundTakingApiActions.notifyTimeAdded);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
exports.handleWebSocketMessage = handleWebSocketMessage;
|
|
82
|
+
|
|
83
|
+
var createQuizTimerWebSocketMiddleware = function createQuizTimerWebSocketMiddleware() {
|
|
84
|
+
var createWebsocketFn = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : createWebsocket;
|
|
85
|
+
var featureOnFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : _featureCheck.featureOn;
|
|
86
|
+
var socket = null;
|
|
87
|
+
return function (store) {
|
|
88
|
+
return function (next) {
|
|
89
|
+
return function (action) {
|
|
90
|
+
var addTimeToExistingQuizSessionsEnabled = featureOnFn('add_time_to_existing_quiz_sessions');
|
|
91
|
+
|
|
92
|
+
if (!addTimeToExistingQuizSessionsEnabled) {
|
|
93
|
+
return next(action);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (action.type === _quizCommon.SET_WS_TOKEN && socket === null) {
|
|
97
|
+
var token = action.payload.wsToken;
|
|
98
|
+
socket = initializeWebSocket(token, createWebsocketFn);
|
|
99
|
+
|
|
100
|
+
if (!socket) {
|
|
101
|
+
return next(action);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var boundTakingApiActions = (0, _redux.bindActionCreators)(takingApiActions, store.dispatch);
|
|
105
|
+
socket.addEventListener('message', function (webSocketMessage) {
|
|
106
|
+
handleWebSocketMessage(webSocketMessage, boundTakingApiActions, handleExtraTime);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (action.type === _quizCommon.QUIZ_SUBMISSION_EVENT && socket !== null) {
|
|
111
|
+
socket.close();
|
|
112
|
+
socket = null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return next(action);
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
exports.createQuizTimerWebSocketMiddleware = createQuizTimerWebSocketMiddleware;
|
|
@@ -61,6 +61,10 @@ var updateTimerInfo = function updateTimerInfo(state, payload) {
|
|
|
61
61
|
return state.mergeIn(['timerData'], payload);
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
var setWSToken = function setWSToken(state, payload) {
|
|
65
|
+
return state.set('wsToken', payload === null || payload === void 0 ? void 0 : payload.wsToken);
|
|
66
|
+
};
|
|
67
|
+
|
|
64
68
|
var updateTimerMinimized = function updateTimerMinimized(state, payload) {
|
|
65
69
|
return state.setIn(['timerData', 'timerMinimized'], payload.timerMinimized);
|
|
66
70
|
};
|
|
@@ -134,6 +138,9 @@ var _default = function _default() {
|
|
|
134
138
|
case _quizCommon.UPDATE_TIMER_INFO:
|
|
135
139
|
return updateTimerInfo(state, action.payload);
|
|
136
140
|
|
|
141
|
+
case _quizCommon.SET_WS_TOKEN:
|
|
142
|
+
return setWSToken(state, action.payload);
|
|
143
|
+
|
|
137
144
|
case _quizCommon.SET_CURRENT_SESSION_ITEM_POSITION:
|
|
138
145
|
return setCurrentSessionItemPosition(state, action.payload);
|
|
139
146
|
|
|
@@ -20,7 +20,7 @@ var MAX_SETTIMEOUT_IN_MILLISECONDS = 20 * 24 * 3600 * 1000; // ms of 20 days
|
|
|
20
20
|
exports.MAX_SETTIMEOUT_IN_MILLISECONDS = MAX_SETTIMEOUT_IN_MILLISECONDS;
|
|
21
21
|
|
|
22
22
|
var TimerService = /*#__PURE__*/function () {
|
|
23
|
-
function TimerService(getTimeRemaining, updateTimeRemaining, getTimerData, submitQuiz, alert) {
|
|
23
|
+
function TimerService(getTimeRemaining, updateTimeRemaining, getTimerData, submitQuiz, alert, setWsToken) {
|
|
24
24
|
var _this = this;
|
|
25
25
|
|
|
26
26
|
(0, _classCallCheck2.default)(this, TimerService);
|
|
@@ -38,6 +38,10 @@ var TimerService = /*#__PURE__*/function () {
|
|
|
38
38
|
_this.__onTimerChange(data);
|
|
39
39
|
|
|
40
40
|
_this.__setApiCallIntervalTime(data.timeLeftInSeconds);
|
|
41
|
+
|
|
42
|
+
if (_this._setWsToken) {
|
|
43
|
+
_this._setWsToken(data.wsToken);
|
|
44
|
+
}
|
|
41
45
|
});
|
|
42
46
|
};
|
|
43
47
|
|
|
@@ -46,6 +50,7 @@ var TimerService = /*#__PURE__*/function () {
|
|
|
46
50
|
this._getTimerData = getTimerData;
|
|
47
51
|
this._submitQuiz = submitQuiz;
|
|
48
52
|
this._alert = alert;
|
|
53
|
+
this._setWsToken = setWsToken;
|
|
49
54
|
this._stopped = false;
|
|
50
55
|
this._apiInterval = null;
|
|
51
56
|
this._clockInterval = null;
|
package/lib/configureStore.js
CHANGED
|
@@ -30,6 +30,8 @@ var _crossDomainMessagingMiddleware = _interopRequireDefault(require("./common/m
|
|
|
30
30
|
|
|
31
31
|
var _eventLoggerMiddleware = _interopRequireDefault(require("./common/middleware/eventLoggerMiddleware.js"));
|
|
32
32
|
|
|
33
|
+
var _quizTimerWebSocketMiddleware = require("./common/middleware/quizTimerWebSocketMiddleware.js");
|
|
34
|
+
|
|
33
35
|
var _alerts = _interopRequireDefault(require("./common/reducers/alerts.js"));
|
|
34
36
|
|
|
35
37
|
var _banks = _interopRequireDefault(require("./common/reducers/banks.js"));
|
|
@@ -129,7 +131,8 @@ function configureStore(_ref) {
|
|
|
129
131
|
}
|
|
130
132
|
});
|
|
131
133
|
var kinesisMiddleware = (0, _eventLoggerMiddleware.default)();
|
|
132
|
-
var
|
|
134
|
+
var websocketMiddleware = (0, _quizTimerWebSocketMiddleware.createQuizTimerWebSocketMiddleware)();
|
|
135
|
+
var baseMiddleware = [_alertTimerMiddleware.default, _crossDomainMessagingMiddleware.default, _reduxMulti.default, kinesisMiddleware, _reduxThunk.default, websocketMiddleware];
|
|
133
136
|
var devMiddleware = [loggerMiddleware];
|
|
134
137
|
var middlewareToApply = inDevelopment ? (0, _union.default)(baseMiddleware, devMiddleware) : baseMiddleware;
|
|
135
138
|
middlewareToApply = (0, _union.default)(middlewareToApply, extraMiddleware);
|
package/lib/taking/api/taking.js
CHANGED
|
@@ -13,6 +13,8 @@ exports.onQuizSessionReadyForTaking = onQuizSessionReadyForTaking;
|
|
|
13
13
|
exports.showSubmitConfirmModal = showSubmitConfirmModal;
|
|
14
14
|
exports.closeSubmitConfirmModal = closeSubmitConfirmModal;
|
|
15
15
|
exports.submitQuiz = submitQuiz;
|
|
16
|
+
exports.notifyTimeAdded = notifyTimeAdded;
|
|
17
|
+
exports.updateTime = updateTime;
|
|
16
18
|
exports.postQuizSession = postQuizSession;
|
|
17
19
|
exports.getTimeRemaining = getTimeRemaining;
|
|
18
20
|
exports.recordQuizTime = recordQuizTime;
|
|
@@ -299,6 +301,24 @@ function submitQuiz(quizSessionId) {
|
|
|
299
301
|
};
|
|
300
302
|
}
|
|
301
303
|
|
|
304
|
+
function notifyTimeAdded(extraTime) {
|
|
305
|
+
return function (dispatch) {
|
|
306
|
+
return dispatch((0, _alerts.addInfo)((0, _formatMessage.default)('Your original time limit has been extended by { extraTime }', {
|
|
307
|
+
extraTime: extraTime
|
|
308
|
+
})));
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function updateTime(timeInSeconds) {
|
|
313
|
+
return function (dispatch) {
|
|
314
|
+
return dispatch((0, _taking.updateTimerInfo)({
|
|
315
|
+
timeLeftInSeconds: timeInSeconds,
|
|
316
|
+
timerRunning: true,
|
|
317
|
+
timeRecording: true
|
|
318
|
+
}));
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
302
322
|
function getQuestionsNeedingAResponse(state, quizSessionId) {
|
|
303
323
|
var responses = state.getIn(['taking', 'responses'], (0, _immutable.Map)());
|
|
304
324
|
var sessionItems = state.get('sessionItems', (0, _immutable.Map)()).filter(function (record) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/quiz-core",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.18.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "The Quiz React SDK by Instructure Inc.",
|
|
6
6
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"@instructure/emotion": "^8.51.0",
|
|
45
45
|
"@instructure/grading-utils": "^1.0.0",
|
|
46
46
|
"@instructure/outcomes-ui": "^3.2.2",
|
|
47
|
-
"@instructure/quiz-common": "20.
|
|
48
|
-
"@instructure/quiz-i18n": "20.
|
|
49
|
-
"@instructure/quiz-interactions": "20.
|
|
50
|
-
"@instructure/quiz-number-input": "20.
|
|
51
|
-
"@instructure/quiz-rce": "20.
|
|
47
|
+
"@instructure/quiz-common": "^20.18.0",
|
|
48
|
+
"@instructure/quiz-i18n": "^20.18.0",
|
|
49
|
+
"@instructure/quiz-interactions": "^20.18.0",
|
|
50
|
+
"@instructure/quiz-number-input": "^20.18.0",
|
|
51
|
+
"@instructure/quiz-rce": "^20.18.0",
|
|
52
52
|
"@instructure/ui-a11y-content": "^8.51.0",
|
|
53
53
|
"@instructure/ui-alerts": "^8.51.0",
|
|
54
54
|
"@instructure/ui-avatar": "^8.51.0",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"file-saver": "~2.0.5",
|
|
112
112
|
"humps": "^2.0.0",
|
|
113
113
|
"immutable": "^3.8.1",
|
|
114
|
-
"instructure-validations": "20.
|
|
114
|
+
"instructure-validations": "^20.18.0",
|
|
115
115
|
"ipaddr.js": "^1.5.4",
|
|
116
116
|
"isomorphic-fetch": "^2.2.0",
|
|
117
117
|
"isuuid": "^0.1.0",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"uuid": "^3.2.1"
|
|
143
143
|
},
|
|
144
144
|
"devDependencies": {
|
|
145
|
-
"@instructure/quiz-scripts": "20.
|
|
145
|
+
"@instructure/quiz-scripts": "^20.18.0",
|
|
146
146
|
"@instructure/ui-axe-check": "^8.51.0",
|
|
147
147
|
"@instructure/ui-babel-preset": "^7.22.1",
|
|
148
148
|
"@instructure/ui-scripts": "^7.22.1",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"jquery": "^2.2.3",
|
|
162
162
|
"karma-junit-reporter": "^2.0.1",
|
|
163
163
|
"most-subject": "^5.3.0",
|
|
164
|
-
"quiz-presets": "20.
|
|
164
|
+
"quiz-presets": "^20.18.0",
|
|
165
165
|
"react": "^16.8.6",
|
|
166
166
|
"react-addons-test-utils": "^15.6.2",
|
|
167
167
|
"react-dom": "^16.8.6",
|
|
@@ -177,5 +177,5 @@
|
|
|
177
177
|
"publishConfig": {
|
|
178
178
|
"access": "public"
|
|
179
179
|
},
|
|
180
|
-
"gitHead": "
|
|
180
|
+
"gitHead": "912c7147e9aa9aaca1ec571cbb2afb0f28023a6c"
|
|
181
181
|
}
|