@seafile/sdoc-editor 0.1.117 → 0.1.119
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/dist/basic-sdk/extension/plugins/sdoc-link/helpers.js +15 -0
- package/dist/basic-sdk/extension/plugins/sdoc-link/menu/index.css +4 -4
- package/dist/basic-sdk/extension/plugins/sdoc-link/menu/index.js +18 -4
- package/dist/basic-sdk/extension/plugins/sdoc-link/menu/sdoc-link-hover-menu.js +15 -14
- package/dist/basic-sdk/extension/plugins/sdoc-link/render-elem.js +4 -2
- package/dist/basic-sdk/socket/socket-client.js +7 -10
- package/dist/basic-sdk/socket/socket-manager.js +144 -96
- package/dist/basic-sdk/socket/with-socket-io.js +1 -1
- package/dist/pages/simple-editor.js +1 -1
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
|
2
2
|
import { ReactEditor } from '@seafile/slate-react';
|
|
3
3
|
import { Editor, Transforms, Range } from '@seafile/slate';
|
|
4
4
|
import slugid from 'slugid';
|
|
5
|
+
import copy from 'copy-to-clipboard';
|
|
5
6
|
import context from '../../../../context';
|
|
6
7
|
import { SDOC_LINK, LINK, INSERT_FILE_DISPLAY_TYPE } from '../../constants';
|
|
7
8
|
import { getNodeType, getSelectedElems } from '../../core';
|
|
@@ -121,4 +122,18 @@ export var getNewFileListData = function getNewFileListData(fileListData, indexI
|
|
|
121
122
|
};
|
|
122
123
|
export var getUrl = function getUrl(uuid) {
|
|
123
124
|
return context.getSdocLocalFileUrl(uuid);
|
|
125
|
+
};
|
|
126
|
+
export var onCopySdocLinkNode = function onCopySdocLinkNode(editor, element) {
|
|
127
|
+
if (editor.selection == null || Range.isExpanded(editor.selection)) return;
|
|
128
|
+
var p = ReactEditor.findPath(editor, element);
|
|
129
|
+
Transforms.select(editor, p);
|
|
130
|
+
var newData = editor.setFragmentData(new DataTransfer());
|
|
131
|
+
copy('copy', {
|
|
132
|
+
onCopy: function onCopy(clipboardData) {
|
|
133
|
+
newData.types.forEach(function (type) {
|
|
134
|
+
var data = newData.getData(type);
|
|
135
|
+
clipboardData.setData(type, data);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
124
139
|
};
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
.sdoc-link-menu .sdoc-link-menu-popover {
|
|
20
20
|
position: absolute;
|
|
21
21
|
top: 36px;
|
|
22
|
-
left:
|
|
22
|
+
left: -15px;
|
|
23
23
|
padding: 8px 0;
|
|
24
24
|
background-color: #fff;
|
|
25
25
|
border: 1px solid #e5e6e8;
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
align-items: flex-start;
|
|
31
31
|
z-index: 101;
|
|
32
32
|
white-space: nowrap;
|
|
33
|
-
width:
|
|
33
|
+
width: 138px;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
.sdoc-link-menu .sdoc-link-menu-popover .sdoc-link-menu-item {
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
user-select: none;
|
|
41
41
|
display: flex;
|
|
42
42
|
align-items: center;
|
|
43
|
-
font-size:
|
|
43
|
+
font-size: 14px;
|
|
44
44
|
color: #212529;
|
|
45
|
-
padding-left:
|
|
45
|
+
padding-left: 5px;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
.sdoc-link-menu .sdoc-link-menu-popover .sdoc-link-menu-item > span :first-child {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
3
|
-
import React, { useCallback, useState } from 'react';
|
|
3
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
4
4
|
import { withTranslation } from 'react-i18next';
|
|
5
5
|
import { SDOC_LINK, MENUS_CONFIG_MAP } from '../../../constants';
|
|
6
6
|
import { MenuItem } from '../../../commons';
|
|
@@ -30,13 +30,13 @@ var SdocLinkMenu = function SdocLinkMenu(_ref) {
|
|
|
30
30
|
}, []);
|
|
31
31
|
var onInsertFileDialogToggle = useCallback(function () {
|
|
32
32
|
setShowInsertPopover(true);
|
|
33
|
-
// setShowInsertDialog(!showInsertDialog);
|
|
34
33
|
}, []);
|
|
35
|
-
var onMouseDown = useCallback(function () {
|
|
34
|
+
var onMouseDown = useCallback(function (e) {
|
|
35
|
+
e.stopPropagation();
|
|
36
36
|
onInsertFileDialogToggle();
|
|
37
37
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
38
38
|
}, []);
|
|
39
|
-
var onInsertSdocFile = useCallback(function () {
|
|
39
|
+
var onInsertSdocFile = useCallback(function (e) {
|
|
40
40
|
setShowInsertPopover(false);
|
|
41
41
|
setShowInsertDialog(true);
|
|
42
42
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -44,6 +44,20 @@ var SdocLinkMenu = function SdocLinkMenu(_ref) {
|
|
|
44
44
|
var onDialogToggle = useCallback(function (value) {
|
|
45
45
|
setShowInsertDialog(value);
|
|
46
46
|
}, []);
|
|
47
|
+
var onHidenInsertPopover = useCallback(function () {
|
|
48
|
+
setShowInsertPopover(false);
|
|
49
|
+
}, []);
|
|
50
|
+
useEffect(function () {
|
|
51
|
+
if (showInsertPopover) {
|
|
52
|
+
window.addEventListener('click', onHidenInsertPopover);
|
|
53
|
+
} else {
|
|
54
|
+
window.removeEventListener('click', onHidenInsertPopover);
|
|
55
|
+
}
|
|
56
|
+
return function () {
|
|
57
|
+
window.removeEventListener('click', onHidenInsertPopover);
|
|
58
|
+
};
|
|
59
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
+
}, [showInsertPopover]);
|
|
47
61
|
var menuConfig = MENUS_CONFIG_MAP[SDOC_LINK];
|
|
48
62
|
var props = _objectSpread(_objectSpread({
|
|
49
63
|
isRichEditor: isRichEditor,
|
|
@@ -2,37 +2,37 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
|
2
2
|
import React, { useCallback, useState } from 'react';
|
|
3
3
|
import { Transforms } from '@seafile/slate';
|
|
4
4
|
import { ReactEditor } from '@seafile/slate-react';
|
|
5
|
-
import copy from 'copy-to-clipboard';
|
|
6
5
|
import { withTranslation } from 'react-i18next';
|
|
7
6
|
import { ElementPopover } from '../../../commons';
|
|
8
7
|
import toaster from '../../../../../components/toast';
|
|
9
|
-
import { getUrl } from '../helpers';
|
|
8
|
+
import { getUrl, onCopySdocLinkNode } from '../helpers';
|
|
10
9
|
import './sdoc-link-hover-menu.css';
|
|
11
10
|
var SdocLinkHoverMenu = function SdocLinkHoverMenu(_ref) {
|
|
12
11
|
var editor = _ref.editor,
|
|
13
12
|
menuPosition = _ref.menuPosition,
|
|
14
13
|
element = _ref.element,
|
|
15
14
|
onUnwrapFileLinkNode = _ref.onUnwrapFileLinkNode,
|
|
15
|
+
onHideInsertHoverMenu = _ref.onHideInsertHoverMenu,
|
|
16
16
|
t = _ref.t;
|
|
17
17
|
var _useState = useState(false),
|
|
18
18
|
_useState2 = _slicedToArray(_useState, 2),
|
|
19
19
|
isShowDisplayStylePopover = _useState2[0],
|
|
20
20
|
setIsShowDisplayStylePopover = _useState2[1];
|
|
21
|
-
var onCopy = useCallback(function () {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
});
|
|
21
|
+
var onCopy = useCallback(function (e) {
|
|
22
|
+
e.stopPropagation();
|
|
23
|
+
onCopySdocLinkNode(editor, element);
|
|
25
24
|
toaster.success(t('Copied'), {
|
|
26
25
|
hasCloseButton: false,
|
|
27
26
|
duration: 2
|
|
28
27
|
});
|
|
28
|
+
onHideInsertHoverMenu();
|
|
29
29
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
30
30
|
}, []);
|
|
31
31
|
var onShowProver = useCallback(function (e) {
|
|
32
|
-
e.stopPropagation();
|
|
33
32
|
setIsShowDisplayStylePopover(true);
|
|
34
33
|
}, []);
|
|
35
|
-
var onSelect = useCallback(function (value) {
|
|
34
|
+
var onSelect = useCallback(function (e, value) {
|
|
35
|
+
e.stopPropagation();
|
|
36
36
|
var path = ReactEditor.findPath(editor, element);
|
|
37
37
|
if (path) {
|
|
38
38
|
Transforms.setNodes(editor, {
|
|
@@ -41,6 +41,7 @@ var SdocLinkHoverMenu = function SdocLinkHoverMenu(_ref) {
|
|
|
41
41
|
at: path
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
+
onHideInsertHoverMenu();
|
|
44
45
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
46
|
}, []);
|
|
46
47
|
return /*#__PURE__*/React.createElement(ElementPopover, null, /*#__PURE__*/React.createElement("div", {
|
|
@@ -84,8 +85,8 @@ var SdocLinkHoverMenu = function SdocLinkHoverMenu(_ref) {
|
|
|
84
85
|
className: "sdoc-file-display-style-popover"
|
|
85
86
|
}, /*#__PURE__*/React.createElement("div", {
|
|
86
87
|
className: "sdoc-file-display-style-item ".concat(element.display_type === 'text_link' ? 'active' : 'inactive'),
|
|
87
|
-
onClick: function onClick() {
|
|
88
|
-
onSelect('text_link');
|
|
88
|
+
onClick: function onClick(e) {
|
|
89
|
+
onSelect(e, 'text_link');
|
|
89
90
|
}
|
|
90
91
|
}, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("i", {
|
|
91
92
|
className: "sdocfont sdoc-text-link"
|
|
@@ -93,8 +94,8 @@ var SdocLinkHoverMenu = function SdocLinkHoverMenu(_ref) {
|
|
|
93
94
|
className: "sdocfont sdoc-check-mark"
|
|
94
95
|
}))), /*#__PURE__*/React.createElement("div", {
|
|
95
96
|
className: "sdoc-file-display-style-item ".concat(element.display_type === 'icon_link' ? 'active' : 'inactive'),
|
|
96
|
-
onClick: function onClick() {
|
|
97
|
-
onSelect('icon_link');
|
|
97
|
+
onClick: function onClick(e) {
|
|
98
|
+
onSelect(e, 'icon_link');
|
|
98
99
|
}
|
|
99
100
|
}, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("i", {
|
|
100
101
|
className: "sdocfont sdoc-inline-link"
|
|
@@ -102,8 +103,8 @@ var SdocLinkHoverMenu = function SdocLinkHoverMenu(_ref) {
|
|
|
102
103
|
className: "sdocfont sdoc-check-mark"
|
|
103
104
|
}))), /*#__PURE__*/React.createElement("div", {
|
|
104
105
|
className: "sdoc-file-display-style-item ".concat(element.display_type === 'card_link' ? 'active' : 'inactive'),
|
|
105
|
-
onClick: function onClick() {
|
|
106
|
-
onSelect('card_link');
|
|
106
|
+
onClick: function onClick(e) {
|
|
107
|
+
onSelect(e, 'card_link');
|
|
107
108
|
}
|
|
108
109
|
}, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("i", {
|
|
109
110
|
className: "sdocfont sdoc-card-link"
|
|
@@ -69,6 +69,7 @@ var SdocFileLink = function SdocFileLink(_ref) {
|
|
|
69
69
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
70
|
}, [isShowInsertHoverMenu]);
|
|
71
71
|
var onClickFile = useCallback(function (e) {
|
|
72
|
+
e.stopPropagation();
|
|
72
73
|
setPosition(e.currentTarget);
|
|
73
74
|
setIsShowInsertHoverMenu(true);
|
|
74
75
|
setTimeout(function () {
|
|
@@ -76,7 +77,7 @@ var SdocFileLink = function SdocFileLink(_ref) {
|
|
|
76
77
|
}, 0);
|
|
77
78
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
78
79
|
}, []);
|
|
79
|
-
var onHideInsertHoverMenu = useCallback(function () {
|
|
80
|
+
var onHideInsertHoverMenu = useCallback(function (e) {
|
|
80
81
|
setIsShowInsertHoverMenu(false);
|
|
81
82
|
unregisterEventHandle();
|
|
82
83
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -107,7 +108,8 @@ var SdocFileLink = function SdocFileLink(_ref) {
|
|
|
107
108
|
editor: editor,
|
|
108
109
|
menuPosition: menuPosition,
|
|
109
110
|
element: element,
|
|
110
|
-
onUnwrapFileLinkNode: onUnwrapFileLinkNode
|
|
111
|
+
onUnwrapFileLinkNode: onUnwrapFileLinkNode,
|
|
112
|
+
onHideInsertHoverMenu: onHideInsertHoverMenu
|
|
111
113
|
}));
|
|
112
114
|
};
|
|
113
115
|
var renderSdocLink = function renderSdocLink(props, editor) {
|
|
@@ -25,11 +25,8 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
|
|
|
25
25
|
// sync operations or document
|
|
26
26
|
if (_this.isReconnect) {
|
|
27
27
|
_this.isReconnect = false;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (serverVersion !== clientVersion) {
|
|
31
|
-
_this.syncDocumentBySocket();
|
|
32
|
-
}
|
|
28
|
+
// The reconnect of socketManager needs to be triggered after entering the room again
|
|
29
|
+
socketManager.onReconnect(result);
|
|
33
30
|
}
|
|
34
31
|
socketManager.dispatchConnectState('onConnected', result);
|
|
35
32
|
return;
|
|
@@ -92,14 +89,14 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
|
|
|
92
89
|
* receive remote broadcast operations
|
|
93
90
|
* @param {*} params {operations, version}
|
|
94
91
|
*/
|
|
95
|
-
this.
|
|
92
|
+
this.onReceiveRemoteOperations = function (params) {
|
|
96
93
|
debug('********* receive operations *********');
|
|
97
94
|
debug('%O', params);
|
|
98
95
|
debug('**************************************');
|
|
99
96
|
var socketManager = SocketManager.getInstance();
|
|
100
|
-
socketManager.
|
|
97
|
+
socketManager.onReceiveRemoteOperations(params);
|
|
101
98
|
};
|
|
102
|
-
this.
|
|
99
|
+
this.getRecentOperations = function () {
|
|
103
100
|
var docUuid = _this.config.docUuid;
|
|
104
101
|
var socketManager = SocketManager.getInstance();
|
|
105
102
|
var clientVersion = socketManager.getDocumentVersion();
|
|
@@ -108,7 +105,7 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
|
|
|
108
105
|
version: clientVersion
|
|
109
106
|
}, function (result) {
|
|
110
107
|
if (result.success) {
|
|
111
|
-
socketManager.
|
|
108
|
+
socketManager.onGetRecentOperations(result);
|
|
112
109
|
}
|
|
113
110
|
});
|
|
114
111
|
};
|
|
@@ -139,7 +136,7 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
|
|
|
139
136
|
this.socket.on('connect_error', this.onConnectError);
|
|
140
137
|
this.socket.on('join-room', this.onJoinRoom);
|
|
141
138
|
this.socket.on('leave-room', this.onLeaveRoom);
|
|
142
|
-
this.socket.on('update-document', this.
|
|
139
|
+
this.socket.on('update-document', this.onReceiveRemoteOperations);
|
|
143
140
|
this.socket.on('update-cursor', this.receiveCursorLocation);
|
|
144
141
|
this.socket.io.on('reconnect', this.onReconnect);
|
|
145
142
|
this.socket.io.on('reconnect_attempt', this.onReconnectAttempt);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
|
|
1
2
|
import _createClass from "@babel/runtime/helpers/esm/createClass";
|
|
2
3
|
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
|
|
3
4
|
import EventBus from '../utils/event-bus';
|
|
@@ -5,31 +6,43 @@ import { syncRemoteOperations, reExecRevertOperationList, revertOperationList, s
|
|
|
5
6
|
import SocketClient from './socket-client';
|
|
6
7
|
import debug from '../utils/debug';
|
|
7
8
|
import { deleteCursor } from '../cursor/helper';
|
|
9
|
+
|
|
10
|
+
// idle --> sending --> conflict --> idle
|
|
11
|
+
// --> conflict --> idle
|
|
12
|
+
// --> disconnect --> conflict --> idle
|
|
13
|
+
// --> idle
|
|
14
|
+
var STATE = {
|
|
15
|
+
IDLE: 'idle',
|
|
16
|
+
SENDING: 'sending',
|
|
17
|
+
CONFLICT: 'conflict',
|
|
18
|
+
DISCONNECT: 'disconnect',
|
|
19
|
+
NEED_RELOAD: 'need_reload'
|
|
20
|
+
};
|
|
8
21
|
var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, document, config) {
|
|
9
22
|
var _this = this;
|
|
10
23
|
_classCallCheck(this, SocketManager);
|
|
11
|
-
this.
|
|
24
|
+
this.getDocumentVersion = function () {
|
|
25
|
+
var version = _this.document.version;
|
|
26
|
+
return version;
|
|
27
|
+
};
|
|
28
|
+
this.onReceiveLocalOperations = function (operations) {
|
|
12
29
|
_this.pendingOperationList.push(operations);
|
|
13
30
|
if (_this.pendingOperationList.length > 5) {
|
|
14
31
|
_this.dispatchConnectState('pending_operations_exceed_limit');
|
|
15
32
|
}
|
|
16
33
|
_this.sendOperations();
|
|
17
34
|
};
|
|
18
|
-
this.getDocumentVersion = function () {
|
|
19
|
-
var version = _this.document.version;
|
|
20
|
-
return version;
|
|
21
|
-
};
|
|
22
35
|
this.sendOperations = function () {
|
|
23
|
-
if (_this.
|
|
24
|
-
_this.
|
|
36
|
+
if (_this.state !== STATE.IDLE) return;
|
|
37
|
+
_this.state = STATE.SENDING;
|
|
25
38
|
_this.sendNextOperations();
|
|
26
39
|
};
|
|
27
40
|
this.sendNextOperations = function () {
|
|
41
|
+
if (_this.state !== STATE.SENDING) return;
|
|
28
42
|
if (_this.pendingOperationList.length === 0) {
|
|
29
|
-
_this.
|
|
43
|
+
_this.state = STATE.IDLE;
|
|
30
44
|
return;
|
|
31
45
|
}
|
|
32
|
-
if (_this._sendingOperations || _this.disconnect) return;
|
|
33
46
|
_this.dispatchConnectState('is-saving');
|
|
34
47
|
var version = _this.document.version;
|
|
35
48
|
var operations = _this.pendingOperationList.shift();
|
|
@@ -38,113 +51,97 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
38
51
|
_this.socketClient.sendOperations(operations, version, selection, _this.sendOperationsCallback);
|
|
39
52
|
};
|
|
40
53
|
this.sendOperationsCallback = function (result) {
|
|
41
|
-
_this._sendingOperations = null;
|
|
42
54
|
if (result && result.success) {
|
|
43
55
|
var serverVersion = result.version;
|
|
44
56
|
_this.document['version'] = serverVersion;
|
|
45
57
|
var lastSavedAt = new Date().getTime();
|
|
46
58
|
_this.dispatchConnectState('saved', lastSavedAt);
|
|
59
|
+
|
|
60
|
+
// send next operations
|
|
61
|
+
_this._sendingOperations = null;
|
|
47
62
|
_this.sendNextOperations();
|
|
48
63
|
return;
|
|
49
64
|
}
|
|
50
65
|
|
|
51
66
|
// Operations are execute failure
|
|
52
|
-
var error_type = result.error_type
|
|
53
|
-
operations = result.operations;
|
|
67
|
+
var error_type = result.error_type;
|
|
54
68
|
if (error_type === 'version_behind_server') {
|
|
55
69
|
// Put the failed operation into the pending list and re-execute it
|
|
56
|
-
_this.pendingOperationList.unshift(
|
|
57
|
-
_this.
|
|
70
|
+
_this.pendingOperationList.unshift(_toConsumableArray(_this._sendingOperations));
|
|
71
|
+
_this.state = STATE.CONFLICT;
|
|
72
|
+
var lose_operations = result.lose_operations;
|
|
73
|
+
_this.resolveConflicting(lose_operations);
|
|
58
74
|
} else if (error_type === 'document_content_load_failed') {
|
|
59
75
|
// After a short-term reconnection, the content of the document fails to load
|
|
60
76
|
_this.dispatchConnectState(error_type);
|
|
77
|
+
|
|
78
|
+
// reset sending control
|
|
79
|
+
_this.state = STATE.NEED_RELOAD;
|
|
80
|
+
_this._sendingOperations = null;
|
|
61
81
|
} else if (error_type === 'Internal_server_error') {
|
|
62
82
|
_this.dispatchConnectState(error_type);
|
|
83
|
+
|
|
84
|
+
// reset sending control
|
|
85
|
+
_this.state = STATE.NEED_RELOAD;
|
|
86
|
+
_this._sendingOperations = null;
|
|
63
87
|
} else if (error_type === 'operation_exec_error') {
|
|
64
88
|
_this.editor.isRemote = true;
|
|
65
|
-
|
|
89
|
+
var dupSendingOperations = _toConsumableArray(_this._sendingOperations);
|
|
90
|
+
revertOperationList(_this.editor, [dupSendingOperations]);
|
|
66
91
|
|
|
67
|
-
//
|
|
92
|
+
// Set isRemote to false must be in Promise.resolve function, make sure the modification of isRemote is later than the onChange event
|
|
68
93
|
Promise.resolve().then(function (_) {
|
|
69
94
|
_this.editor.isRemote = false;
|
|
70
95
|
_this.dispatchConnectState(error_type);
|
|
96
|
+
|
|
97
|
+
// send next operations
|
|
98
|
+
_this._sendingOperations = null;
|
|
71
99
|
_this.sendNextOperations();
|
|
72
100
|
});
|
|
73
101
|
}
|
|
74
102
|
};
|
|
75
|
-
this.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
103
|
+
this.onReceiveRemoteOperations = function (params) {
|
|
104
|
+
// if this.disconnect is true, Then the message sent by the remote end cannot be received
|
|
105
|
+
if (_this.state !== STATE.IDLE) return;
|
|
106
|
+
var serverVersion = params.version;
|
|
107
|
+
var clientVersion = _this.document.version;
|
|
108
|
+
if (serverVersion === clientVersion + 1) {
|
|
109
|
+
// update execute remote operations flag
|
|
110
|
+
_this.editor.isRemote = true;
|
|
111
|
+
debug('Editor isRemote is true: %s', _this.editor.isRemote);
|
|
112
|
+
var operations = params.operations;
|
|
113
|
+
// Update content & version
|
|
114
|
+
debug('execute remote operations: %O', operations);
|
|
115
|
+
syncRemoteOperations(_this.editor, operations);
|
|
116
|
+
|
|
117
|
+
// Update document
|
|
118
|
+
_this.document.version = serverVersion;
|
|
119
|
+
_this.document.children = _this.editor.children;
|
|
120
|
+
Promise.resolve().then(function () {
|
|
121
|
+
_this.editor.isRemote = false;
|
|
122
|
+
_this.revertOperationList = [];
|
|
81
123
|
});
|
|
124
|
+
} else {
|
|
125
|
+
// isConflict
|
|
126
|
+
_this.onConflictHappen();
|
|
82
127
|
}
|
|
83
|
-
if (_this.isExecRemoteOperations) return;
|
|
84
|
-
|
|
85
|
-
// update execute remote operations flag
|
|
86
|
-
_this.isExecRemoteOperations = true;
|
|
87
|
-
_this.editor.isRemote = true;
|
|
88
|
-
_this.execRemoteOperations();
|
|
89
128
|
};
|
|
90
|
-
this.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// When performing remote operations, the revert operation is only required for the first time
|
|
97
|
-
if (_this.revertOperationList.length === 0) {
|
|
98
|
-
// 1. Revert operations
|
|
99
|
-
// 1.1 record reverted operationList & clear pendingOperationList
|
|
100
|
-
_this.revertOperationList = _this.pendingOperationList.slice();
|
|
101
|
-
_this.pendingOperationList = [];
|
|
102
|
-
|
|
103
|
-
// 1.2 Revert operationList
|
|
104
|
-
debug('revert locale operations: %O', _this.revertOperationList);
|
|
105
|
-
revertOperationList(_this.editor, _this.revertOperationList);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 2. execute operations
|
|
109
|
-
var operations = params.operations;
|
|
110
|
-
// 2.1 Update content & version
|
|
111
|
-
debug('execute remote operations: %O', operations);
|
|
112
|
-
syncRemoteOperations(_this.editor, operations);
|
|
113
|
-
|
|
114
|
-
// 2.2 Update document
|
|
115
|
-
_this.document.version = serverVersion;
|
|
116
|
-
_this.document.children = _this.editor.children;
|
|
117
|
-
|
|
118
|
-
// Execute next operations
|
|
119
|
-
if (_this.remoteOperationsList.length > 0) {
|
|
120
|
-
_this.execRemoteOperations();
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Re-execute undone operations after isRemote is set to false
|
|
125
|
-
// Need resend this operations to server
|
|
126
|
-
Promise.resolve().then(function (_) {
|
|
127
|
-
// reset execute remote operations flag
|
|
128
|
-
_this.isExecRemoteOperations = false;
|
|
129
|
-
_this.editor.isRemote = false;
|
|
130
|
-
|
|
131
|
-
// 3. Execute pending operations
|
|
132
|
-
// 3.1 Re-execute operations
|
|
133
|
-
debug('Editor isRemote is false: %s', _this.editor.isRemote);
|
|
134
|
-
debug('Re-execute pending operations, %O', _this.revertOperationList);
|
|
135
|
-
reExecRevertOperationList(_this.editor, _this.revertOperationList);
|
|
136
|
-
|
|
137
|
-
// 3.2 Clear revert operationList
|
|
138
|
-
_this.revertOperationList = [];
|
|
139
|
-
});
|
|
140
|
-
} else {
|
|
141
|
-
// conflict:
|
|
142
|
-
// remote operations is not empty, waiting until remote operations all executed
|
|
143
|
-
debug('remote operations is not empty, and local version is not match remote version.');
|
|
144
|
-
}
|
|
129
|
+
this.onReconnect = function (result) {
|
|
130
|
+
var serverVersion = result.version;
|
|
131
|
+
var clientVersion = _this.getDocumentVersion();
|
|
132
|
+
// The client version is inconsistent with the server version, and the latest operations performed by the server need to be loaded
|
|
133
|
+
if (serverVersion !== clientVersion) {
|
|
134
|
+
_this.onConflictHappen();
|
|
145
135
|
}
|
|
136
|
+
|
|
137
|
+
// The version consistency indicates that there is no conflict and no processing is required
|
|
138
|
+
_this.state = STATE.IDLE;
|
|
139
|
+
};
|
|
140
|
+
this.onConflictHappen = function () {
|
|
141
|
+
_this.state = STATE.CONFLICT;
|
|
142
|
+
_this.socketClient.getRecentOperations();
|
|
146
143
|
};
|
|
147
|
-
this.
|
|
144
|
+
this.onGetRecentOperations = function (result) {
|
|
148
145
|
var mode = result.mode,
|
|
149
146
|
content = result.content;
|
|
150
147
|
// sync document
|
|
@@ -158,6 +155,8 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
158
155
|
_this.editor.isRemote = true;
|
|
159
156
|
_this.editor.onChange();
|
|
160
157
|
_this.editor.isRemote = false;
|
|
158
|
+
_this.state = STATE.IDLE;
|
|
159
|
+
_this._sendingOperations = null;
|
|
161
160
|
|
|
162
161
|
// 2. exec client operationList
|
|
163
162
|
var pendingOperationList = _this.pendingOperationList.slice();
|
|
@@ -169,14 +168,67 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
169
168
|
return;
|
|
170
169
|
}
|
|
171
170
|
|
|
172
|
-
// sync operations
|
|
171
|
+
// mode os operations: sync operations
|
|
173
172
|
// content is [{version, operations}, {version, operations}, ...]
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
var loseOperations = content;
|
|
174
|
+
_this.resolveConflicting(loseOperations);
|
|
175
|
+
};
|
|
176
|
+
this.resolveConflicting = function (loseOperations) {
|
|
178
177
|
_this.editor.isRemote = true;
|
|
179
|
-
_this.
|
|
178
|
+
debug('Editor isRemote is true: %s', _this.editor.isRemote);
|
|
179
|
+
if (_this.pendingOperationList.length !== 0) {
|
|
180
|
+
// 1. Revert operations
|
|
181
|
+
// 1.1 record reverted operationList & clear pendingOperationList
|
|
182
|
+
_this.revertOperationList = _this.pendingOperationList.slice();
|
|
183
|
+
_this.pendingOperationList = [];
|
|
184
|
+
|
|
185
|
+
// 1.2 Revert operationList
|
|
186
|
+
debug('revert locale operations: %O', _this.revertOperationList);
|
|
187
|
+
revertOperationList(_this.editor, _this.revertOperationList);
|
|
188
|
+
}
|
|
189
|
+
loseOperations = loseOperations.sort(function (prev, next) {
|
|
190
|
+
return prev.version - next.version;
|
|
191
|
+
});
|
|
192
|
+
debug('lose operations length: %s', loseOperations.length);
|
|
193
|
+
while (loseOperations.length > 0) {
|
|
194
|
+
var operationParams = loseOperations.shift();
|
|
195
|
+
// 2. execute operations
|
|
196
|
+
var operations = operationParams.operations,
|
|
197
|
+
serverVersion = operationParams.version;
|
|
198
|
+
// 2.1 Update content & version
|
|
199
|
+
debug('execute lose operations: %O', operations);
|
|
200
|
+
syncRemoteOperations(_this.editor, operations);
|
|
201
|
+
|
|
202
|
+
// 2.2 Update document
|
|
203
|
+
_this.document.version = serverVersion;
|
|
204
|
+
_this.document.children = _this.editor.children;
|
|
205
|
+
}
|
|
206
|
+
if (_this.revertOperationList.length === 0) {
|
|
207
|
+
Promise.resolve().then(function () {
|
|
208
|
+
_this.editor.isRemote = false;
|
|
209
|
+
_this.state = STATE.IDLE;
|
|
210
|
+
_this._sendingOperations = null;
|
|
211
|
+
_this.revertOperationList = [];
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Set isRemote to false must be in Promise.resolve function, make sure the modification of isRemote is later than the onChange event
|
|
217
|
+
Promise.resolve().then(function () {
|
|
218
|
+
// reset execute remote operations flag
|
|
219
|
+
_this.editor.isRemote = false;
|
|
220
|
+
_this.state = STATE.IDLE;
|
|
221
|
+
_this._sendingOperations = null;
|
|
222
|
+
|
|
223
|
+
// 3. Execute pending operations
|
|
224
|
+
// 3.1 Re-execute operations
|
|
225
|
+
debug('Editor isRemote is false: %s', _this.editor.isRemote);
|
|
226
|
+
debug('Re-execute pending operations, %O', _this.revertOperationList);
|
|
227
|
+
reExecRevertOperationList(_this.editor, _this.revertOperationList);
|
|
228
|
+
|
|
229
|
+
// 3.2 Clear revert operationList
|
|
230
|
+
_this.revertOperationList = [];
|
|
231
|
+
});
|
|
180
232
|
};
|
|
181
233
|
this.sendCursorLocation = function (location) {
|
|
182
234
|
_this.socketClient.sendCursorLocation(location);
|
|
@@ -198,16 +250,12 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
198
250
|
_this.pendingOperationList.unshift(_this._sendingOperations.slice());
|
|
199
251
|
_this._sendingOperations = null;
|
|
200
252
|
}
|
|
201
|
-
_this.
|
|
202
|
-
_this.disconnect = true;
|
|
253
|
+
_this.state = STATE.DISCONNECT;
|
|
203
254
|
|
|
204
255
|
// Update saved state
|
|
205
256
|
var lastSavedAt = new Date().getTime();
|
|
206
257
|
_this.dispatchConnectState('saved', lastSavedAt);
|
|
207
258
|
}
|
|
208
|
-
if (type === 'reconnect') {
|
|
209
|
-
_this.disconnect = false;
|
|
210
|
-
}
|
|
211
259
|
_this.eventBus.dispatch(type, message);
|
|
212
260
|
};
|
|
213
261
|
this.closeSocketConnect = function () {
|
|
@@ -216,11 +264,11 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
216
264
|
this.editor = editor;
|
|
217
265
|
this.document = document;
|
|
218
266
|
this.socketClient = new SocketClient(config);
|
|
219
|
-
this.isSending = false;
|
|
220
267
|
this.pendingOperationList = []; // Two-dimensional arrays: [operations, operations, ...]
|
|
221
268
|
this.remoteOperationsList = []; // Same with pending operations
|
|
222
269
|
this.revertOperationList = [];
|
|
223
270
|
this.eventBus = EventBus.getInstance();
|
|
271
|
+
this.state = STATE.IDLE;
|
|
224
272
|
});
|
|
225
273
|
SocketManager.getInstance = function (editor, document, socketConfig) {
|
|
226
274
|
if (SocketManager.instance) {
|
|
@@ -30,7 +30,7 @@ var withSocketIO = function withSocketIO(editor, options) {
|
|
|
30
30
|
var updateOperations = operations.filter(function (operation) {
|
|
31
31
|
return operation.type !== 'set_selection';
|
|
32
32
|
});
|
|
33
|
-
_socketManager.
|
|
33
|
+
_socketManager.onReceiveLocalOperations(updateOperations);
|
|
34
34
|
}
|
|
35
35
|
_socketManager.sendCursorLocation(editor.selection);
|
|
36
36
|
}
|
|
@@ -141,7 +141,7 @@ var SimpleEditor = function SimpleEditor(_ref) {
|
|
|
141
141
|
className: "error-page"
|
|
142
142
|
}, /*#__PURE__*/React.createElement("div", {
|
|
143
143
|
className: "error-tip"
|
|
144
|
-
}, t(errorMessage))), !isLoadingSdoc && /*#__PURE__*/React.createElement(React.Fragment, null, isShowChanges ? /*#__PURE__*/React.createElement(DiffViewer, {
|
|
144
|
+
}, t(errorMessage))), !isLoadingSdoc && !errorMessage && /*#__PURE__*/React.createElement(React.Fragment, null, isShowChanges ? /*#__PURE__*/React.createElement(DiffViewer, {
|
|
145
145
|
currentContent: currentContent,
|
|
146
146
|
lastContent: lastContent
|
|
147
147
|
}) : /*#__PURE__*/React.createElement(SDocEditor, {
|