@elice/material-exercise 1.250618.0 → 1.250711.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/cjs/components/material-exercise/exercise-file-editor/ExerciseFileEditor.d.ts +3 -5
- package/cjs/components/material-exercise/exercise-file-editor/ExerciseFileEditor.js +58 -20
- package/cjs/components/material-exercise/exercise-file-editor/ExerciseFileReadOnlyBanner.d.ts +5 -2
- package/cjs/components/material-exercise/exercise-file-editor/ExerciseFileReadOnlyBanner.js +1 -8
- package/cjs/components/material-exercise/exercise-file-editor/locales/en.json.js +1 -1
- package/cjs/components/material-exercise/exercise-file-editor/locales/ja.json.js +1 -1
- package/cjs/components/material-exercise/exercise-file-editor/locales/ko.json.js +1 -1
- package/cjs/components/material-exercise/exercise-file-editor/locales/th.json.js +1 -1
- package/cjs/components/material-exercise/exercise-runner/ExerciseRunnerControllerStatusMessage.js +5 -0
- package/cjs/hooks/useUsercodeEditWebSocket.d.ts +6 -1
- package/cjs/hooks/useUsercodeEditWebSocket.js +5 -3
- package/es/components/material-exercise/exercise-file-editor/ExerciseFileEditor.d.ts +3 -5
- package/es/components/material-exercise/exercise-file-editor/ExerciseFileEditor.js +59 -21
- package/es/components/material-exercise/exercise-file-editor/ExerciseFileReadOnlyBanner.d.ts +5 -2
- package/es/components/material-exercise/exercise-file-editor/ExerciseFileReadOnlyBanner.js +1 -8
- package/es/components/material-exercise/exercise-file-editor/locales/en.json.js +1 -1
- package/es/components/material-exercise/exercise-file-editor/locales/ja.json.js +1 -1
- package/es/components/material-exercise/exercise-file-editor/locales/ko.json.js +1 -1
- package/es/components/material-exercise/exercise-file-editor/locales/th.json.js +1 -1
- package/es/components/material-exercise/exercise-runner/ExerciseRunnerControllerStatusMessage.js +5 -0
- package/es/hooks/useUsercodeEditWebSocket.d.ts +6 -1
- package/es/hooks/useUsercodeEditWebSocket.js +5 -3
- package/package.json +4 -4
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
declare const ExerciseFileEditor: React.VFC;
|
|
6
|
-
export default ExerciseFileEditor;
|
|
2
|
+
import type { WithIntlComponentBuilderProps } from '@elice/intl';
|
|
3
|
+
declare const _default: React.ForwardRefExoticComponent<Omit<import("@elice/intl").IntlComponentExtraProps & Omit<WithIntlComponentBuilderProps, keyof import("@elice/intl").IntlComponentExtraProps | "__intl">, "ref"> & React.RefAttributes<any>>;
|
|
4
|
+
export default _default;
|
|
@@ -8,6 +8,7 @@ var React = require('react');
|
|
|
8
8
|
var reactUse = require('react-use');
|
|
9
9
|
var apiClient = require('@elice/api-client');
|
|
10
10
|
var blocks = require('@elice/blocks');
|
|
11
|
+
var intl = require('@elice/intl');
|
|
11
12
|
var types = require('@elice/types');
|
|
12
13
|
var utils = require('@elice/utils');
|
|
13
14
|
var recoil = require('recoil');
|
|
@@ -35,6 +36,8 @@ require('humps');
|
|
|
35
36
|
require('ot-text-unicode');
|
|
36
37
|
var useUsercodeEditWebSocket = require('../../../hooks/useUsercodeEditWebSocket.js');
|
|
37
38
|
var ExerciseFileReadOnlyBanner = require('./ExerciseFileReadOnlyBanner.js');
|
|
39
|
+
var en = require('./locales/en.json.js');
|
|
40
|
+
var ko = require('./locales/ko.json.js');
|
|
38
41
|
|
|
39
42
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
40
43
|
|
|
@@ -46,7 +49,8 @@ var React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
|
46
49
|
/**
|
|
47
50
|
* Exercise file (code) editor for editable files
|
|
48
51
|
*/
|
|
49
|
-
var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
52
|
+
var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
53
|
+
var __intl = _ref.__intl;
|
|
50
54
|
var _React$useContext = React__default.default.useContext(context.ExerciseContext),
|
|
51
55
|
materialExerciseId = _React$useContext.materialExerciseId,
|
|
52
56
|
exerciseRoomId = _React$useContext.exerciseRoomId,
|
|
@@ -104,7 +108,7 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
104
108
|
* Set value of current file.
|
|
105
109
|
*/
|
|
106
110
|
var setValue = /*#__PURE__*/function () {
|
|
107
|
-
var
|
|
111
|
+
var _ref2 = _rollupPluginBabelHelpers.asyncToGenerator( /*#__PURE__*/_rollupPluginBabelHelpers.regeneratorRuntime().mark(function _callee(content) {
|
|
108
112
|
var _a, readyExerciseImage;
|
|
109
113
|
return _rollupPluginBabelHelpers.regeneratorRuntime().wrap(function _callee$(_context) {
|
|
110
114
|
while (1) switch (_context.prev = _context.next) {
|
|
@@ -162,7 +166,7 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
162
166
|
}, _callee);
|
|
163
167
|
}));
|
|
164
168
|
return function setValue(_x) {
|
|
165
|
-
return
|
|
169
|
+
return _ref2.apply(this, arguments);
|
|
166
170
|
};
|
|
167
171
|
}();
|
|
168
172
|
/**
|
|
@@ -224,6 +228,9 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
224
228
|
_iterator.f();
|
|
225
229
|
}
|
|
226
230
|
};
|
|
231
|
+
/**
|
|
232
|
+
* Handle `WRITE_SUCCEED` event.
|
|
233
|
+
*/
|
|
227
234
|
var handleWebsocketUsercodeWriteSucceed = function handleWebsocketUsercodeWriteSucceed(msg, requestId) {
|
|
228
235
|
var request = usercodeSyncRequestsRef.current.get(requestId);
|
|
229
236
|
if (request) {
|
|
@@ -231,6 +238,16 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
231
238
|
request.onSucceed(msg);
|
|
232
239
|
}
|
|
233
240
|
};
|
|
241
|
+
/**
|
|
242
|
+
* Handle `WRITE_FAILED` event.
|
|
243
|
+
*/
|
|
244
|
+
var handleWebsocketUsercodeWriteFailedAndReset = function handleWebsocketUsercodeWriteFailedAndReset(msg) {
|
|
245
|
+
blocks.Notification.error(__intl.formatMessage({
|
|
246
|
+
id: 'materialExercise.websocket.writeFailedAndReset'
|
|
247
|
+
}), {
|
|
248
|
+
duration: 5000
|
|
249
|
+
});
|
|
250
|
+
};
|
|
234
251
|
/**
|
|
235
252
|
* Usercode edit web socket.
|
|
236
253
|
*/
|
|
@@ -238,10 +255,16 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
238
255
|
exerciseRoomId: exerciseRoomId,
|
|
239
256
|
filename: activeFilename,
|
|
240
257
|
onInit: function onInit() {
|
|
241
|
-
|
|
258
|
+
setWebSocketReady(false);
|
|
259
|
+
serverInitiatedVersions.current = [];
|
|
260
|
+
editorDocHistories.current = {};
|
|
261
|
+
setTimeout(function () {
|
|
262
|
+
return setWebSocketReady(true);
|
|
263
|
+
}, 100);
|
|
242
264
|
},
|
|
243
265
|
onWriteNoti: handleWebsocketUsercodeWriteNoti,
|
|
244
|
-
onWriteSucceed: handleWebsocketUsercodeWriteSucceed
|
|
266
|
+
onWriteSucceed: handleWebsocketUsercodeWriteSucceed,
|
|
267
|
+
onWriteFailedAndReset: handleWebsocketUsercodeWriteFailedAndReset
|
|
245
268
|
});
|
|
246
269
|
/**
|
|
247
270
|
*
|
|
@@ -312,7 +335,6 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
312
335
|
// Handle editor content change.
|
|
313
336
|
//
|
|
314
337
|
React__default.default.useEffect(function () {
|
|
315
|
-
var _a, _b;
|
|
316
338
|
var _editorApis = editorApis.current;
|
|
317
339
|
if (!_editorApis || !isReady) {
|
|
318
340
|
return;
|
|
@@ -320,19 +342,24 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
320
342
|
_editorApis.setValue(wsUsercode.doc);
|
|
321
343
|
pushEditorDocHistories();
|
|
322
344
|
var subscription = undefined;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
345
|
+
var __subscribe = function __subscribe() {
|
|
346
|
+
var _a, _b;
|
|
347
|
+
if (utils.FlutterApp.checkWebview()) {
|
|
348
|
+
subscription = (_a = _editorApis.editor) === null || _a === void 0 ? void 0 : _a.onDidChangeModelContent(function (e) {
|
|
349
|
+
pushEditorDocHistories();
|
|
350
|
+
handleChange(e);
|
|
351
|
+
utils.FlutterApp.onDidChangeContent();
|
|
352
|
+
});
|
|
353
|
+
} else {
|
|
354
|
+
subscription = (_b = _editorApis.editor) === null || _b === void 0 ? void 0 : _b.onDidChangeModelContent(function (e) {
|
|
355
|
+
pushEditorDocHistories();
|
|
356
|
+
handleChange(e);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
var timeout = window.setTimeout(__subscribe, 10); // need to wait for editor to be ready.
|
|
335
361
|
return function () {
|
|
362
|
+
window.clearTimeout(timeout);
|
|
336
363
|
if (subscription) {
|
|
337
364
|
subscription.dispose();
|
|
338
365
|
}
|
|
@@ -437,7 +464,14 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
437
464
|
column: true,
|
|
438
465
|
width: "100%",
|
|
439
466
|
height: "100%",
|
|
440
|
-
|
|
467
|
+
style: {
|
|
468
|
+
// When the user focuses out and in again,
|
|
469
|
+
// Monaco editor can't restore focus well in Safari when parent's user-select is none.
|
|
470
|
+
WebkitUserSelect: !readOnly ? 'text' : undefined
|
|
471
|
+
},
|
|
472
|
+
children: [jsxRuntime.jsx(ExerciseFileReadOnlyBanner.default, {
|
|
473
|
+
__intl: __intl
|
|
474
|
+
}), jsxRuntime.jsx(MonacoEditorLazy.default, {
|
|
441
475
|
defaultValue: "",
|
|
442
476
|
filename: editorFilename,
|
|
443
477
|
markers: wsUsercode.markers,
|
|
@@ -455,5 +489,9 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
455
489
|
}, editorFilename)]
|
|
456
490
|
});
|
|
457
491
|
};
|
|
492
|
+
//
|
|
493
|
+
//
|
|
494
|
+
//
|
|
495
|
+
var ExerciseFileEditor$1 = new intl.IntlComponentBuilder(ExerciseFileEditor).add('ko', ko.default).add('en', en.default).addAsync('th', Promise.resolve().then(function () { return require('./locales/th.json.js'); })).addAsync('ja', Promise.resolve().then(function () { return require('./locales/ja.json.js'); })).build();
|
|
458
496
|
|
|
459
|
-
exports.default = ExerciseFileEditor;
|
|
497
|
+
exports.default = ExerciseFileEditor$1;
|
package/cjs/components/material-exercise/exercise-file-editor/ExerciseFileReadOnlyBanner.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { WithIntlComponentBuilderProps as EliceIntlProps } from '@elice/intl';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Banner for readonly exercise file.
|
|
5
|
+
*/
|
|
6
|
+
declare const ExerciseFileReadOnlyBanner: React.FC<EliceIntlProps>;
|
|
7
|
+
export default ExerciseFileReadOnlyBanner;
|
|
@@ -6,15 +6,12 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var blocks = require('@elice/blocks');
|
|
8
8
|
var designTokens = require('@elice/design-tokens');
|
|
9
|
-
var intl = require('@elice/intl');
|
|
10
9
|
var styled = require('styled-components');
|
|
11
10
|
require('../context/recoil.js');
|
|
12
11
|
var context = require('../context/context.js');
|
|
13
12
|
require('../context/recoilTypes.js');
|
|
14
13
|
require('../context/subjects.js');
|
|
15
14
|
require('../context/ExerciseProvider.js');
|
|
16
|
-
var en = require('./locales/en.json.js');
|
|
17
|
-
var ko = require('./locales/ko.json.js');
|
|
18
15
|
|
|
19
16
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
20
17
|
|
|
@@ -44,9 +41,5 @@ var ExerciseFileReadOnlyBanner = function ExerciseFileReadOnlyBanner(_ref) {
|
|
|
44
41
|
})
|
|
45
42
|
});
|
|
46
43
|
};
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
var ExerciseFileReadOnlyBanner$1 = new intl.IntlComponentBuilder(ExerciseFileReadOnlyBanner).add('ko', ko.default).add('en', en.default).addAsync('th', Promise.resolve().then(function () { return require('./locales/th.json.js'); })).addAsync('ja', Promise.resolve().then(function () { return require('./locales/ja.json.js'); })).build();
|
|
51
44
|
|
|
52
|
-
exports.default = ExerciseFileReadOnlyBanner
|
|
45
|
+
exports.default = ExerciseFileReadOnlyBanner;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var messageEn = {"materialExercise.text.readOnlyBanner":"[Read-Only] This file is read-only."};
|
|
5
|
+
var messageEn = {"materialExercise.text.readOnlyBanner":"[Read-Only] This file is read-only.","materialExercise.websocket.writeFailedAndReset":"Failed to save the code and the content has been reset. Please try again shortly."};
|
|
6
6
|
|
|
7
7
|
exports.default = messageEn;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var ja = {"materialExercise.text.readOnlyBanner":"[読み取り専用] このファイルは読み取り専用です。"};
|
|
5
|
+
var ja = {"materialExercise.text.readOnlyBanner":"[読み取り専用] このファイルは読み取り専用です。","materialExercise.websocket.writeFailedAndReset":"コードの保存に失敗し、内容が初期化されました。しばらくしてから再度お試しください。"};
|
|
6
6
|
|
|
7
7
|
exports.default = ja;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var messageKo = {"materialExercise.text.readOnlyBanner":"[읽기전용] 이 파일은 읽기전용 입니다."};
|
|
5
|
+
var messageKo = {"materialExercise.text.readOnlyBanner":"[읽기전용] 이 파일은 읽기전용 입니다.","materialExercise.websocket.writeFailedAndReset":"코드 저장에 실패하여 내용이 초기화되었습니다. 잠시 후 다시 시도해주세요."};
|
|
6
6
|
|
|
7
7
|
exports.default = messageKo;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var th = {"materialExercise.text.readOnlyBanner":"[อ่านเท่านั้น] ไฟล์นี้เป็นแบบอ่านเท่านั้น"};
|
|
5
|
+
var th = {"materialExercise.text.readOnlyBanner":"[อ่านเท่านั้น] ไฟล์นี้เป็นแบบอ่านเท่านั้น","materialExercise.websocket.writeFailedAndReset":"ไม่สามารถบันทึกรหัสได้ เนื้อหาได้ถูกรีเซ็ต กรุณาลองใหม่อีกครั้งในภายหลัง"};
|
|
6
6
|
|
|
7
7
|
exports.default = th;
|
package/cjs/components/material-exercise/exercise-runner/ExerciseRunnerControllerStatusMessage.js
CHANGED
|
@@ -8,6 +8,7 @@ var React = require('react');
|
|
|
8
8
|
var blocks = require('@elice/blocks');
|
|
9
9
|
var intl = require('@elice/intl');
|
|
10
10
|
var websocket = require('@elice/websocket');
|
|
11
|
+
var react = require('@emotion/react');
|
|
11
12
|
var recoil = require('recoil');
|
|
12
13
|
var styled = require('styled-components');
|
|
13
14
|
var recoil$1 = require('../context/recoil.js');
|
|
@@ -25,6 +26,7 @@ var StyledMessageWrapper = styled__default.default(blocks.Flex).withConfig({
|
|
|
25
26
|
componentId: "sc-1yqi8o2-0"
|
|
26
27
|
})(["height:1rem;"]);
|
|
27
28
|
var ExerciseRunnerControllerStatusMessage = function ExerciseRunnerControllerStatusMessage() {
|
|
29
|
+
var theme = react.useTheme();
|
|
28
30
|
var intl$1 = intl.useRawEliceIntl();
|
|
29
31
|
var websocketStatus = recoil.useRecoilValue(recoil$1.exerciseWebSocketTotalStatusQuery(['usercodeEdit', 'runnerRoom', 'stdio']));
|
|
30
32
|
var _useState = React.useState(false),
|
|
@@ -66,6 +68,9 @@ var ExerciseRunnerControllerStatusMessage = function ExerciseRunnerControllerSta
|
|
|
66
68
|
title: intl$1.formatMessage({
|
|
67
69
|
id: 'exerciseRunner.controller.tooltip.closed'
|
|
68
70
|
}),
|
|
71
|
+
overlayStyle: {
|
|
72
|
+
zIndex: theme.zIndex.tooltip
|
|
73
|
+
},
|
|
69
74
|
children: jsxRuntime.jsxs(StyledMessageWrapper, {
|
|
70
75
|
align: "center",
|
|
71
76
|
children: [jsxRuntime.jsx(ExerciseRunnerControllerStatusIndicator.default, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WebsocketUsercodeMessageWriteSucceed as WSUsercodeMessageWriteSucceed } from '@elice/types';
|
|
1
|
+
import type { WebsocketUsercodeMessageWriteFailed as WSUsercodeMessageWriteFailed, WebsocketUsercodeMessageWriteSucceed as WSUsercodeMessageWriteSucceed } from '@elice/types';
|
|
2
2
|
import type { TextOp } from 'ot-text-unicode';
|
|
3
3
|
interface CursorRange {
|
|
4
4
|
from: number;
|
|
@@ -22,6 +22,10 @@ export type UseUsercodeEditWebSocketPropOnWriteNoti = (ots: TextOp, isFromServer
|
|
|
22
22
|
* Event handler for `WRITE_SUCCEED` event.
|
|
23
23
|
*/
|
|
24
24
|
export type UseUsercodeEditWebSocketPropOnWriteSucceed = (msg: WSUsercodeMessageWriteSucceed, requestId: number) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Event handler for `WRITE_FAILED` event.
|
|
27
|
+
*/
|
|
28
|
+
export type UseUsercodeEditWebSocketPropOnWriteFailedAndReset = (msg: WSUsercodeMessageWriteFailed) => void;
|
|
25
29
|
/**
|
|
26
30
|
* Props of `useUsercodeEditWebSocket`.
|
|
27
31
|
*/
|
|
@@ -31,6 +35,7 @@ interface UseUsercodeEditWebSocketProps {
|
|
|
31
35
|
onInit: UseUsercodeEditWebSocketPropOnInit;
|
|
32
36
|
onWriteNoti: UseUsercodeEditWebSocketPropOnWriteNoti;
|
|
33
37
|
onWriteSucceed: UseUsercodeEditWebSocketPropOnWriteSucceed;
|
|
38
|
+
onWriteFailedAndReset: UseUsercodeEditWebSocketPropOnWriteFailedAndReset;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
*
|
|
@@ -20,7 +20,7 @@ var randomWords__default = /*#__PURE__*/_interopDefaultCompat(randomWords);
|
|
|
20
20
|
//
|
|
21
21
|
//
|
|
22
22
|
//
|
|
23
|
-
var MAX_WRITE_FAIL_COUNT = 5
|
|
23
|
+
var MAX_WRITE_FAIL_COUNT = 1; // FIXME: change retry count 5 -> 1 to avoid user confusion.
|
|
24
24
|
/**
|
|
25
25
|
*
|
|
26
26
|
*/
|
|
@@ -29,7 +29,8 @@ var useUsercodeEditWebSocket = function useUsercodeEditWebSocket(_ref) {
|
|
|
29
29
|
filename = _ref.filename,
|
|
30
30
|
onInit = _ref.onInit,
|
|
31
31
|
onWriteNoti = _ref.onWriteNoti,
|
|
32
|
-
onWriteSucceed = _ref.onWriteSucceed
|
|
32
|
+
onWriteSucceed = _ref.onWriteSucceed,
|
|
33
|
+
onWriteFailedAndReset = _ref.onWriteFailedAndReset;
|
|
33
34
|
var _return = React.useRef({});
|
|
34
35
|
var _useMaterialConfig = materialSharedUtils.useMaterialConfig(),
|
|
35
36
|
apiWsOriginUrl = _useMaterialConfig.apiWsOriginUrl;
|
|
@@ -342,7 +343,7 @@ var useUsercodeEditWebSocket = function useUsercodeEditWebSocket(_ref) {
|
|
|
342
343
|
wsMessageUpdate(msg);
|
|
343
344
|
break;
|
|
344
345
|
case 'WRITE_FAILED':
|
|
345
|
-
wsMessageWriteFailed();
|
|
346
|
+
wsMessageWriteFailed(msg);
|
|
346
347
|
break;
|
|
347
348
|
case 'WRITE_SUCCEED':
|
|
348
349
|
wsMessageWriteSucceed(msg);
|
|
@@ -418,6 +419,7 @@ var useUsercodeEditWebSocket = function useUsercodeEditWebSocket(_ref) {
|
|
|
418
419
|
writeFailCount.current += 1;
|
|
419
420
|
if (MAX_WRITE_FAIL_COUNT < writeFailCount.current) {
|
|
420
421
|
sendReset();
|
|
422
|
+
onWriteFailedAndReset(msg);
|
|
421
423
|
} else {
|
|
422
424
|
sendFetch();
|
|
423
425
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
declare const ExerciseFileEditor: React.VFC;
|
|
6
|
-
export default ExerciseFileEditor;
|
|
2
|
+
import type { WithIntlComponentBuilderProps } from '@elice/intl';
|
|
3
|
+
declare const _default: React.ForwardRefExoticComponent<Omit<import("@elice/intl").IntlComponentExtraProps & Omit<WithIntlComponentBuilderProps, keyof import("@elice/intl").IntlComponentExtraProps | "__intl">, "ref"> & React.RefAttributes<any>>;
|
|
4
|
+
export default _default;
|
|
@@ -3,7 +3,8 @@ import { jsx, jsxs } from 'react/jsx-runtime';
|
|
|
3
3
|
import React, { useEffect } from 'react';
|
|
4
4
|
import { useDebounce } from 'react-use';
|
|
5
5
|
import { getOrgMaterialExerciseExerciseImageExerciseFileGet } from '@elice/api-client';
|
|
6
|
-
import { Flex } from '@elice/blocks';
|
|
6
|
+
import { Flex, Notification } from '@elice/blocks';
|
|
7
|
+
import { IntlComponentBuilder } from '@elice/intl';
|
|
7
8
|
import { enums } from '@elice/types';
|
|
8
9
|
import { FlutterApp } from '@elice/utils';
|
|
9
10
|
import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil';
|
|
@@ -31,6 +32,8 @@ import 'humps';
|
|
|
31
32
|
import 'ot-text-unicode';
|
|
32
33
|
import { useUsercodeEditWebSocket } from '../../../hooks/useUsercodeEditWebSocket.js';
|
|
33
34
|
import ExerciseFileReadOnlyBanner from './ExerciseFileReadOnlyBanner.js';
|
|
35
|
+
import messageEn from './locales/en.json.js';
|
|
36
|
+
import messageKo from './locales/ko.json.js';
|
|
34
37
|
|
|
35
38
|
//
|
|
36
39
|
//
|
|
@@ -38,7 +41,8 @@ import ExerciseFileReadOnlyBanner from './ExerciseFileReadOnlyBanner.js';
|
|
|
38
41
|
/**
|
|
39
42
|
* Exercise file (code) editor for editable files
|
|
40
43
|
*/
|
|
41
|
-
var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
44
|
+
var ExerciseFileEditor = function ExerciseFileEditor(_ref) {
|
|
45
|
+
var __intl = _ref.__intl;
|
|
42
46
|
var _React$useContext = React.useContext(ExerciseContext),
|
|
43
47
|
materialExerciseId = _React$useContext.materialExerciseId,
|
|
44
48
|
exerciseRoomId = _React$useContext.exerciseRoomId,
|
|
@@ -96,7 +100,7 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
96
100
|
* Set value of current file.
|
|
97
101
|
*/
|
|
98
102
|
var setValue = /*#__PURE__*/function () {
|
|
99
|
-
var
|
|
103
|
+
var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(content) {
|
|
100
104
|
var _a, readyExerciseImage;
|
|
101
105
|
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
102
106
|
while (1) switch (_context.prev = _context.next) {
|
|
@@ -154,7 +158,7 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
154
158
|
}, _callee);
|
|
155
159
|
}));
|
|
156
160
|
return function setValue(_x) {
|
|
157
|
-
return
|
|
161
|
+
return _ref2.apply(this, arguments);
|
|
158
162
|
};
|
|
159
163
|
}();
|
|
160
164
|
/**
|
|
@@ -216,6 +220,9 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
216
220
|
_iterator.f();
|
|
217
221
|
}
|
|
218
222
|
};
|
|
223
|
+
/**
|
|
224
|
+
* Handle `WRITE_SUCCEED` event.
|
|
225
|
+
*/
|
|
219
226
|
var handleWebsocketUsercodeWriteSucceed = function handleWebsocketUsercodeWriteSucceed(msg, requestId) {
|
|
220
227
|
var request = usercodeSyncRequestsRef.current.get(requestId);
|
|
221
228
|
if (request) {
|
|
@@ -223,6 +230,16 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
223
230
|
request.onSucceed(msg);
|
|
224
231
|
}
|
|
225
232
|
};
|
|
233
|
+
/**
|
|
234
|
+
* Handle `WRITE_FAILED` event.
|
|
235
|
+
*/
|
|
236
|
+
var handleWebsocketUsercodeWriteFailedAndReset = function handleWebsocketUsercodeWriteFailedAndReset(msg) {
|
|
237
|
+
Notification.error(__intl.formatMessage({
|
|
238
|
+
id: 'materialExercise.websocket.writeFailedAndReset'
|
|
239
|
+
}), {
|
|
240
|
+
duration: 5000
|
|
241
|
+
});
|
|
242
|
+
};
|
|
226
243
|
/**
|
|
227
244
|
* Usercode edit web socket.
|
|
228
245
|
*/
|
|
@@ -230,10 +247,16 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
230
247
|
exerciseRoomId: exerciseRoomId,
|
|
231
248
|
filename: activeFilename,
|
|
232
249
|
onInit: function onInit() {
|
|
233
|
-
|
|
250
|
+
setWebSocketReady(false);
|
|
251
|
+
serverInitiatedVersions.current = [];
|
|
252
|
+
editorDocHistories.current = {};
|
|
253
|
+
setTimeout(function () {
|
|
254
|
+
return setWebSocketReady(true);
|
|
255
|
+
}, 100);
|
|
234
256
|
},
|
|
235
257
|
onWriteNoti: handleWebsocketUsercodeWriteNoti,
|
|
236
|
-
onWriteSucceed: handleWebsocketUsercodeWriteSucceed
|
|
258
|
+
onWriteSucceed: handleWebsocketUsercodeWriteSucceed,
|
|
259
|
+
onWriteFailedAndReset: handleWebsocketUsercodeWriteFailedAndReset
|
|
237
260
|
});
|
|
238
261
|
/**
|
|
239
262
|
*
|
|
@@ -304,7 +327,6 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
304
327
|
// Handle editor content change.
|
|
305
328
|
//
|
|
306
329
|
React.useEffect(function () {
|
|
307
|
-
var _a, _b;
|
|
308
330
|
var _editorApis = editorApis.current;
|
|
309
331
|
if (!_editorApis || !isReady) {
|
|
310
332
|
return;
|
|
@@ -312,19 +334,24 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
312
334
|
_editorApis.setValue(wsUsercode.doc);
|
|
313
335
|
pushEditorDocHistories();
|
|
314
336
|
var subscription = undefined;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
337
|
+
var __subscribe = function __subscribe() {
|
|
338
|
+
var _a, _b;
|
|
339
|
+
if (FlutterApp.checkWebview()) {
|
|
340
|
+
subscription = (_a = _editorApis.editor) === null || _a === void 0 ? void 0 : _a.onDidChangeModelContent(function (e) {
|
|
341
|
+
pushEditorDocHistories();
|
|
342
|
+
handleChange(e);
|
|
343
|
+
FlutterApp.onDidChangeContent();
|
|
344
|
+
});
|
|
345
|
+
} else {
|
|
346
|
+
subscription = (_b = _editorApis.editor) === null || _b === void 0 ? void 0 : _b.onDidChangeModelContent(function (e) {
|
|
347
|
+
pushEditorDocHistories();
|
|
348
|
+
handleChange(e);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
var timeout = window.setTimeout(__subscribe, 10); // need to wait for editor to be ready.
|
|
327
353
|
return function () {
|
|
354
|
+
window.clearTimeout(timeout);
|
|
328
355
|
if (subscription) {
|
|
329
356
|
subscription.dispose();
|
|
330
357
|
}
|
|
@@ -429,7 +456,14 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
429
456
|
column: true,
|
|
430
457
|
width: "100%",
|
|
431
458
|
height: "100%",
|
|
432
|
-
|
|
459
|
+
style: {
|
|
460
|
+
// When the user focuses out and in again,
|
|
461
|
+
// Monaco editor can't restore focus well in Safari when parent's user-select is none.
|
|
462
|
+
WebkitUserSelect: !readOnly ? 'text' : undefined
|
|
463
|
+
},
|
|
464
|
+
children: [jsx(ExerciseFileReadOnlyBanner, {
|
|
465
|
+
__intl: __intl
|
|
466
|
+
}), jsx(MonacoEditorLazy, {
|
|
433
467
|
defaultValue: "",
|
|
434
468
|
filename: editorFilename,
|
|
435
469
|
markers: wsUsercode.markers,
|
|
@@ -447,5 +481,9 @@ var ExerciseFileEditor = function ExerciseFileEditor() {
|
|
|
447
481
|
}, editorFilename)]
|
|
448
482
|
});
|
|
449
483
|
};
|
|
484
|
+
//
|
|
485
|
+
//
|
|
486
|
+
//
|
|
487
|
+
var ExerciseFileEditor$1 = new IntlComponentBuilder(ExerciseFileEditor).add('ko', messageKo).add('en', messageEn).addAsync('th', import('./locales/th.json.js')).addAsync('ja', import('./locales/ja.json.js')).build();
|
|
450
488
|
|
|
451
|
-
export { ExerciseFileEditor as default };
|
|
489
|
+
export { ExerciseFileEditor$1 as default };
|
package/es/components/material-exercise/exercise-file-editor/ExerciseFileReadOnlyBanner.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { WithIntlComponentBuilderProps as EliceIntlProps } from '@elice/intl';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Banner for readonly exercise file.
|
|
5
|
+
*/
|
|
6
|
+
declare const ExerciseFileReadOnlyBanner: React.FC<EliceIntlProps>;
|
|
7
|
+
export default ExerciseFileReadOnlyBanner;
|
|
@@ -2,15 +2,12 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { Text } from '@elice/blocks';
|
|
4
4
|
import { base } from '@elice/design-tokens';
|
|
5
|
-
import { IntlComponentBuilder } from '@elice/intl';
|
|
6
5
|
import styled from 'styled-components';
|
|
7
6
|
import '../context/recoil.js';
|
|
8
7
|
import { ExerciseContext } from '../context/context.js';
|
|
9
8
|
import '../context/recoilTypes.js';
|
|
10
9
|
import '../context/subjects.js';
|
|
11
10
|
import '../context/ExerciseProvider.js';
|
|
12
|
-
import messageEn from './locales/en.json.js';
|
|
13
|
-
import messageKo from './locales/ko.json.js';
|
|
14
11
|
|
|
15
12
|
var StyledEditorReadOnlyBanner = styled.div.withConfig({
|
|
16
13
|
componentId: "sc-kh9iw2-0"
|
|
@@ -35,9 +32,5 @@ var ExerciseFileReadOnlyBanner = function ExerciseFileReadOnlyBanner(_ref) {
|
|
|
35
32
|
})
|
|
36
33
|
});
|
|
37
34
|
};
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
var ExerciseFileReadOnlyBanner$1 = new IntlComponentBuilder(ExerciseFileReadOnlyBanner).add('ko', messageKo).add('en', messageEn).addAsync('th', import('./locales/th.json.js')).addAsync('ja', import('./locales/ja.json.js')).build();
|
|
42
35
|
|
|
43
|
-
export { ExerciseFileReadOnlyBanner
|
|
36
|
+
export { ExerciseFileReadOnlyBanner as default };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var messageEn = {"materialExercise.text.readOnlyBanner":"[Read-Only] This file is read-only."};
|
|
1
|
+
var messageEn = {"materialExercise.text.readOnlyBanner":"[Read-Only] This file is read-only.","materialExercise.websocket.writeFailedAndReset":"Failed to save the code and the content has been reset. Please try again shortly."};
|
|
2
2
|
|
|
3
3
|
export { messageEn as default };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var ja = {"materialExercise.text.readOnlyBanner":"[読み取り専用] このファイルは読み取り専用です。"};
|
|
1
|
+
var ja = {"materialExercise.text.readOnlyBanner":"[読み取り専用] このファイルは読み取り専用です。","materialExercise.websocket.writeFailedAndReset":"コードの保存に失敗し、内容が初期化されました。しばらくしてから再度お試しください。"};
|
|
2
2
|
|
|
3
3
|
export { ja as default };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var messageKo = {"materialExercise.text.readOnlyBanner":"[읽기전용] 이 파일은 읽기전용 입니다."};
|
|
1
|
+
var messageKo = {"materialExercise.text.readOnlyBanner":"[읽기전용] 이 파일은 읽기전용 입니다.","materialExercise.websocket.writeFailedAndReset":"코드 저장에 실패하여 내용이 초기화되었습니다. 잠시 후 다시 시도해주세요."};
|
|
2
2
|
|
|
3
3
|
export { messageKo as default };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var th = {"materialExercise.text.readOnlyBanner":"[อ่านเท่านั้น] ไฟล์นี้เป็นแบบอ่านเท่านั้น"};
|
|
1
|
+
var th = {"materialExercise.text.readOnlyBanner":"[อ่านเท่านั้น] ไฟล์นี้เป็นแบบอ่านเท่านั้น","materialExercise.websocket.writeFailedAndReset":"ไม่สามารถบันทึกรหัสได้ เนื้อหาได้ถูกรีเซ็ต กรุณาลองใหม่อีกครั้งในภายหลัง"};
|
|
2
2
|
|
|
3
3
|
export { th as default };
|
package/es/components/material-exercise/exercise-runner/ExerciseRunnerControllerStatusMessage.js
CHANGED
|
@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
|
|
|
4
4
|
import { Flex, Tooltip, Text } from '@elice/blocks';
|
|
5
5
|
import { useRawEliceIntl } from '@elice/intl';
|
|
6
6
|
import { EliceWebSocket } from '@elice/websocket';
|
|
7
|
+
import { useTheme } from '@emotion/react';
|
|
7
8
|
import { useRecoilValue } from 'recoil';
|
|
8
9
|
import styled from 'styled-components';
|
|
9
10
|
import { exerciseWebSocketTotalStatusQuery } from '../context/recoil.js';
|
|
@@ -17,6 +18,7 @@ var StyledMessageWrapper = styled(Flex).withConfig({
|
|
|
17
18
|
componentId: "sc-1yqi8o2-0"
|
|
18
19
|
})(["height:1rem;"]);
|
|
19
20
|
var ExerciseRunnerControllerStatusMessage = function ExerciseRunnerControllerStatusMessage() {
|
|
21
|
+
var theme = useTheme();
|
|
20
22
|
var intl = useRawEliceIntl();
|
|
21
23
|
var websocketStatus = useRecoilValue(exerciseWebSocketTotalStatusQuery(['usercodeEdit', 'runnerRoom', 'stdio']));
|
|
22
24
|
var _useState = useState(false),
|
|
@@ -58,6 +60,9 @@ var ExerciseRunnerControllerStatusMessage = function ExerciseRunnerControllerSta
|
|
|
58
60
|
title: intl.formatMessage({
|
|
59
61
|
id: 'exerciseRunner.controller.tooltip.closed'
|
|
60
62
|
}),
|
|
63
|
+
overlayStyle: {
|
|
64
|
+
zIndex: theme.zIndex.tooltip
|
|
65
|
+
},
|
|
61
66
|
children: jsxs(StyledMessageWrapper, {
|
|
62
67
|
align: "center",
|
|
63
68
|
children: [jsx(ExerciseRunnerControllerStatusIndicator, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WebsocketUsercodeMessageWriteSucceed as WSUsercodeMessageWriteSucceed } from '@elice/types';
|
|
1
|
+
import type { WebsocketUsercodeMessageWriteFailed as WSUsercodeMessageWriteFailed, WebsocketUsercodeMessageWriteSucceed as WSUsercodeMessageWriteSucceed } from '@elice/types';
|
|
2
2
|
import type { TextOp } from 'ot-text-unicode';
|
|
3
3
|
interface CursorRange {
|
|
4
4
|
from: number;
|
|
@@ -22,6 +22,10 @@ export type UseUsercodeEditWebSocketPropOnWriteNoti = (ots: TextOp, isFromServer
|
|
|
22
22
|
* Event handler for `WRITE_SUCCEED` event.
|
|
23
23
|
*/
|
|
24
24
|
export type UseUsercodeEditWebSocketPropOnWriteSucceed = (msg: WSUsercodeMessageWriteSucceed, requestId: number) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Event handler for `WRITE_FAILED` event.
|
|
27
|
+
*/
|
|
28
|
+
export type UseUsercodeEditWebSocketPropOnWriteFailedAndReset = (msg: WSUsercodeMessageWriteFailed) => void;
|
|
25
29
|
/**
|
|
26
30
|
* Props of `useUsercodeEditWebSocket`.
|
|
27
31
|
*/
|
|
@@ -31,6 +35,7 @@ interface UseUsercodeEditWebSocketProps {
|
|
|
31
35
|
onInit: UseUsercodeEditWebSocketPropOnInit;
|
|
32
36
|
onWriteNoti: UseUsercodeEditWebSocketPropOnWriteNoti;
|
|
33
37
|
onWriteSucceed: UseUsercodeEditWebSocketPropOnWriteSucceed;
|
|
38
|
+
onWriteFailedAndReset: UseUsercodeEditWebSocketPropOnWriteFailedAndReset;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
*
|
|
@@ -13,7 +13,7 @@ import { uniCount, uniToStrPos } from 'unicount';
|
|
|
13
13
|
//
|
|
14
14
|
//
|
|
15
15
|
//
|
|
16
|
-
var MAX_WRITE_FAIL_COUNT = 5
|
|
16
|
+
var MAX_WRITE_FAIL_COUNT = 1; // FIXME: change retry count 5 -> 1 to avoid user confusion.
|
|
17
17
|
/**
|
|
18
18
|
*
|
|
19
19
|
*/
|
|
@@ -22,7 +22,8 @@ var useUsercodeEditWebSocket = function useUsercodeEditWebSocket(_ref) {
|
|
|
22
22
|
filename = _ref.filename,
|
|
23
23
|
onInit = _ref.onInit,
|
|
24
24
|
onWriteNoti = _ref.onWriteNoti,
|
|
25
|
-
onWriteSucceed = _ref.onWriteSucceed
|
|
25
|
+
onWriteSucceed = _ref.onWriteSucceed,
|
|
26
|
+
onWriteFailedAndReset = _ref.onWriteFailedAndReset;
|
|
26
27
|
var _return = useRef({});
|
|
27
28
|
var _useMaterialConfig = useMaterialConfig(),
|
|
28
29
|
apiWsOriginUrl = _useMaterialConfig.apiWsOriginUrl;
|
|
@@ -335,7 +336,7 @@ var useUsercodeEditWebSocket = function useUsercodeEditWebSocket(_ref) {
|
|
|
335
336
|
wsMessageUpdate(msg);
|
|
336
337
|
break;
|
|
337
338
|
case 'WRITE_FAILED':
|
|
338
|
-
wsMessageWriteFailed();
|
|
339
|
+
wsMessageWriteFailed(msg);
|
|
339
340
|
break;
|
|
340
341
|
case 'WRITE_SUCCEED':
|
|
341
342
|
wsMessageWriteSucceed(msg);
|
|
@@ -411,6 +412,7 @@ var useUsercodeEditWebSocket = function useUsercodeEditWebSocket(_ref) {
|
|
|
411
412
|
writeFailCount.current += 1;
|
|
412
413
|
if (MAX_WRITE_FAIL_COUNT < writeFailCount.current) {
|
|
413
414
|
sendReset();
|
|
415
|
+
onWriteFailedAndReset(msg);
|
|
414
416
|
} else {
|
|
415
417
|
sendFetch();
|
|
416
418
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elice/material-exercise",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.250711.0",
|
|
4
4
|
"description": "User view and editing components of Elice material exercise",
|
|
5
5
|
"repository": "https://git.elicer.io/elice/frontend/library/elice-material",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -77,14 +77,14 @@
|
|
|
77
77
|
"xterm-addon-fit": "^0.5.0"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@elice/api-client": "^1.
|
|
80
|
+
"@elice/api-client": "^1.250702.0",
|
|
81
81
|
"@elice/blocks": "1.241007.0",
|
|
82
82
|
"@elice/design-tokens": "^1.220803.0",
|
|
83
83
|
"@elice/icons": "^1.230814.0",
|
|
84
84
|
"@elice/intl": "0.241127.0",
|
|
85
85
|
"@elice/markdown": "1.241015.0",
|
|
86
|
-
"@elice/material-shared-types": "1.
|
|
87
|
-
"@elice/material-shared-utils": "1.
|
|
86
|
+
"@elice/material-shared-types": "1.250711.0",
|
|
87
|
+
"@elice/material-shared-utils": "1.250711.0",
|
|
88
88
|
"@elice/mui-elements": "^5.250108.0-controllabel.0",
|
|
89
89
|
"@elice/mui-system": "^5.250108.0-controllabel.0",
|
|
90
90
|
"@elice/types": "^1.250612.0-teamleaderboard.1",
|