@seafile/sdoc-editor 0.1.118 → 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.
|
@@ -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);
|
|
@@ -6,6 +6,18 @@ import { syncRemoteOperations, reExecRevertOperationList, revertOperationList, s
|
|
|
6
6
|
import SocketClient from './socket-client';
|
|
7
7
|
import debug from '../utils/debug';
|
|
8
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
|
+
};
|
|
9
21
|
var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, document, config) {
|
|
10
22
|
var _this = this;
|
|
11
23
|
_classCallCheck(this, SocketManager);
|
|
@@ -13,7 +25,7 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
13
25
|
var version = _this.document.version;
|
|
14
26
|
return version;
|
|
15
27
|
};
|
|
16
|
-
this.
|
|
28
|
+
this.onReceiveLocalOperations = function (operations) {
|
|
17
29
|
_this.pendingOperationList.push(operations);
|
|
18
30
|
if (_this.pendingOperationList.length > 5) {
|
|
19
31
|
_this.dispatchConnectState('pending_operations_exceed_limit');
|
|
@@ -21,16 +33,16 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
21
33
|
_this.sendOperations();
|
|
22
34
|
};
|
|
23
35
|
this.sendOperations = function () {
|
|
24
|
-
if (_this.
|
|
25
|
-
_this.
|
|
36
|
+
if (_this.state !== STATE.IDLE) return;
|
|
37
|
+
_this.state = STATE.SENDING;
|
|
26
38
|
_this.sendNextOperations();
|
|
27
39
|
};
|
|
28
40
|
this.sendNextOperations = function () {
|
|
41
|
+
if (_this.state !== STATE.SENDING) return;
|
|
29
42
|
if (_this.pendingOperationList.length === 0) {
|
|
30
|
-
_this.
|
|
43
|
+
_this.state = STATE.IDLE;
|
|
31
44
|
return;
|
|
32
45
|
}
|
|
33
|
-
if (_this._sendingOperations || _this.disconnect) return;
|
|
34
46
|
_this.dispatchConnectState('is-saving');
|
|
35
47
|
var version = _this.document.version;
|
|
36
48
|
var operations = _this.pendingOperationList.shift();
|
|
@@ -40,11 +52,13 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
40
52
|
};
|
|
41
53
|
this.sendOperationsCallback = function (result) {
|
|
42
54
|
if (result && result.success) {
|
|
43
|
-
_this._sendingOperations = null;
|
|
44
55
|
var serverVersion = result.version;
|
|
45
56
|
_this.document['version'] = serverVersion;
|
|
46
57
|
var lastSavedAt = new Date().getTime();
|
|
47
58
|
_this.dispatchConnectState('saved', lastSavedAt);
|
|
59
|
+
|
|
60
|
+
// send next operations
|
|
61
|
+
_this._sendingOperations = null;
|
|
48
62
|
_this.sendNextOperations();
|
|
49
63
|
return;
|
|
50
64
|
}
|
|
@@ -52,154 +66,82 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
52
66
|
// Operations are execute failure
|
|
53
67
|
var error_type = result.error_type;
|
|
54
68
|
if (error_type === 'version_behind_server') {
|
|
55
|
-
var dupSendingOperations = _toConsumableArray(_this._sendingOperations);
|
|
56
69
|
// Put the failed operation into the pending list and re-execute it
|
|
57
|
-
_this.pendingOperationList.unshift(
|
|
70
|
+
_this.pendingOperationList.unshift(_toConsumableArray(_this._sendingOperations));
|
|
71
|
+
_this.state = STATE.CONFLICT;
|
|
58
72
|
var lose_operations = result.lose_operations;
|
|
59
|
-
_this.
|
|
73
|
+
_this.resolveConflicting(lose_operations);
|
|
60
74
|
} else if (error_type === 'document_content_load_failed') {
|
|
61
|
-
_this._sendingOperations = null;
|
|
62
75
|
// After a short-term reconnection, the content of the document fails to load
|
|
63
76
|
_this.dispatchConnectState(error_type);
|
|
64
|
-
|
|
77
|
+
|
|
78
|
+
// reset sending control
|
|
79
|
+
_this.state = STATE.NEED_RELOAD;
|
|
65
80
|
_this._sendingOperations = null;
|
|
81
|
+
} else if (error_type === 'Internal_server_error') {
|
|
66
82
|
_this.dispatchConnectState(error_type);
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
|
|
84
|
+
// reset sending control
|
|
85
|
+
_this.state = STATE.NEED_RELOAD;
|
|
69
86
|
_this._sendingOperations = null;
|
|
87
|
+
} else if (error_type === 'operation_exec_error') {
|
|
70
88
|
_this.editor.isRemote = true;
|
|
71
|
-
|
|
89
|
+
var dupSendingOperations = _toConsumableArray(_this._sendingOperations);
|
|
90
|
+
revertOperationList(_this.editor, [dupSendingOperations]);
|
|
72
91
|
|
|
73
|
-
//
|
|
92
|
+
// Set isRemote to false must be in Promise.resolve function, make sure the modification of isRemote is later than the onChange event
|
|
74
93
|
Promise.resolve().then(function (_) {
|
|
75
94
|
_this.editor.isRemote = false;
|
|
76
95
|
_this.dispatchConnectState(error_type);
|
|
96
|
+
|
|
97
|
+
// send next operations
|
|
98
|
+
_this._sendingOperations = null;
|
|
77
99
|
_this.sendNextOperations();
|
|
78
100
|
});
|
|
79
101
|
}
|
|
80
102
|
};
|
|
81
|
-
this.
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return prev.version - next.version;
|
|
93
|
-
});
|
|
94
|
-
debug('Editor isRemote is true: %s', _this.editor.isRemote);
|
|
95
|
-
while (loseOperations.length > 0) {
|
|
96
|
-
var operationParams = loseOperations.shift();
|
|
97
|
-
// 2. execute operations
|
|
98
|
-
var operations = operationParams.operations,
|
|
99
|
-
serverVersion = operationParams.version;
|
|
100
|
-
// 2.1 Update content & version
|
|
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
|
|
101
114
|
debug('execute remote operations: %O', operations);
|
|
102
115
|
syncRemoteOperations(_this.editor, operations);
|
|
103
116
|
|
|
104
|
-
//
|
|
117
|
+
// Update document
|
|
105
118
|
_this.document.version = serverVersion;
|
|
106
119
|
_this.document.children = _this.editor.children;
|
|
120
|
+
Promise.resolve().then(function () {
|
|
121
|
+
_this.editor.isRemote = false;
|
|
122
|
+
_this.revertOperationList = [];
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
// isConflict
|
|
126
|
+
_this.onConflictHappen();
|
|
107
127
|
}
|
|
108
|
-
|
|
109
|
-
// Re-execute undone operations after isRemote is set to false
|
|
110
|
-
// Need resend this operations to server
|
|
111
|
-
|
|
112
|
-
// reset execute remote operations flag
|
|
113
|
-
_this.editor.isRemote = false;
|
|
114
|
-
_this.isSending = false;
|
|
115
|
-
_this._sendingOperations = null;
|
|
116
|
-
|
|
117
|
-
// 3. Execute pending operations
|
|
118
|
-
// 3.1 Re-execute operations
|
|
119
|
-
debug('Editor isRemote is false: %s', _this.editor.isRemote);
|
|
120
|
-
debug('Re-execute pending operations, %O', _this.revertOperationList);
|
|
121
|
-
reExecRevertOperationList(_this.editor, _this.revertOperationList);
|
|
122
|
-
|
|
123
|
-
// 3.2 Clear revert operationList
|
|
124
|
-
_this.revertOperationList = [];
|
|
125
128
|
};
|
|
126
|
-
this.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
});
|
|
133
|
-
var clientVersion = _this.document.version;
|
|
134
|
-
_this.remoteOperationsList = _this.remoteOperationsList.filter(function (item) {
|
|
135
|
-
return item.version > clientVersion;
|
|
136
|
-
});
|
|
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();
|
|
137
135
|
}
|
|
138
|
-
if (_this.isExecRemoteOperations || _this._sendingOperations) return;
|
|
139
136
|
|
|
140
|
-
//
|
|
141
|
-
_this.
|
|
142
|
-
_this.editor.isRemote = true;
|
|
143
|
-
_this.execRemoteOperations();
|
|
137
|
+
// The version consistency indicates that there is no conflict and no processing is required
|
|
138
|
+
_this.state = STATE.IDLE;
|
|
144
139
|
};
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
var serverVersion = params.version;
|
|
149
|
-
var clientVersion = _this.document.version;
|
|
150
|
-
if (serverVersion === clientVersion + 1) {
|
|
151
|
-
// When performing remote operations, the revert operation is only required for the first time
|
|
152
|
-
if (_this.revertOperationList.length === 0) {
|
|
153
|
-
// 1. Revert operations
|
|
154
|
-
// 1.1 record reverted operationList & clear pendingOperationList
|
|
155
|
-
_this.revertOperationList = _this.pendingOperationList.slice();
|
|
156
|
-
_this.pendingOperationList = [];
|
|
157
|
-
|
|
158
|
-
// 1.2 Revert operationList
|
|
159
|
-
debug('revert locale operations: %O', _this.revertOperationList);
|
|
160
|
-
revertOperationList(_this.editor, _this.revertOperationList);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// 2. execute operations
|
|
164
|
-
var operations = params.operations;
|
|
165
|
-
// 2.1 Update content & version
|
|
166
|
-
debug('execute remote operations: %O', operations);
|
|
167
|
-
syncRemoteOperations(_this.editor, operations);
|
|
168
|
-
|
|
169
|
-
// 2.2 Update document
|
|
170
|
-
_this.document.version = serverVersion;
|
|
171
|
-
_this.document.children = _this.editor.children;
|
|
172
|
-
|
|
173
|
-
// Execute next operations
|
|
174
|
-
if (_this.remoteOperationsList.length > 0) {
|
|
175
|
-
_this.execRemoteOperations();
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Re-execute undone operations after isRemote is set to false
|
|
180
|
-
// Need resend this operations to server
|
|
181
|
-
Promise.resolve().then(function (_) {
|
|
182
|
-
// reset execute remote operations flag
|
|
183
|
-
_this.isExecRemoteOperations = false;
|
|
184
|
-
_this.editor.isRemote = false;
|
|
185
|
-
|
|
186
|
-
// 3. Execute pending operations
|
|
187
|
-
// 3.1 Re-execute operations
|
|
188
|
-
debug('Editor isRemote is false: %s', _this.editor.isRemote);
|
|
189
|
-
debug('Re-execute pending operations, %O', _this.revertOperationList);
|
|
190
|
-
reExecRevertOperationList(_this.editor, _this.revertOperationList);
|
|
191
|
-
|
|
192
|
-
// 3.2 Clear revert operationList
|
|
193
|
-
_this.revertOperationList = [];
|
|
194
|
-
});
|
|
195
|
-
} else {
|
|
196
|
-
// conflict:
|
|
197
|
-
// remote operations is not empty, waiting until remote operations all executed
|
|
198
|
-
debug('remote operations is not empty, and local version is not match remote version.');
|
|
199
|
-
}
|
|
200
|
-
}
|
|
140
|
+
this.onConflictHappen = function () {
|
|
141
|
+
_this.state = STATE.CONFLICT;
|
|
142
|
+
_this.socketClient.getRecentOperations();
|
|
201
143
|
};
|
|
202
|
-
this.
|
|
144
|
+
this.onGetRecentOperations = function (result) {
|
|
203
145
|
var mode = result.mode,
|
|
204
146
|
content = result.content;
|
|
205
147
|
// sync document
|
|
@@ -213,6 +155,8 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
213
155
|
_this.editor.isRemote = true;
|
|
214
156
|
_this.editor.onChange();
|
|
215
157
|
_this.editor.isRemote = false;
|
|
158
|
+
_this.state = STATE.IDLE;
|
|
159
|
+
_this._sendingOperations = null;
|
|
216
160
|
|
|
217
161
|
// 2. exec client operationList
|
|
218
162
|
var pendingOperationList = _this.pendingOperationList.slice();
|
|
@@ -224,14 +168,67 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
224
168
|
return;
|
|
225
169
|
}
|
|
226
170
|
|
|
227
|
-
// sync operations
|
|
171
|
+
// mode os operations: sync operations
|
|
228
172
|
// content is [{version, operations}, {version, operations}, ...]
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
173
|
+
var loseOperations = content;
|
|
174
|
+
_this.resolveConflicting(loseOperations);
|
|
175
|
+
};
|
|
176
|
+
this.resolveConflicting = function (loseOperations) {
|
|
233
177
|
_this.editor.isRemote = true;
|
|
234
|
-
_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
|
+
});
|
|
235
232
|
};
|
|
236
233
|
this.sendCursorLocation = function (location) {
|
|
237
234
|
_this.socketClient.sendCursorLocation(location);
|
|
@@ -253,16 +250,12 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
253
250
|
_this.pendingOperationList.unshift(_this._sendingOperations.slice());
|
|
254
251
|
_this._sendingOperations = null;
|
|
255
252
|
}
|
|
256
|
-
_this.
|
|
257
|
-
_this.disconnect = true;
|
|
253
|
+
_this.state = STATE.DISCONNECT;
|
|
258
254
|
|
|
259
255
|
// Update saved state
|
|
260
256
|
var lastSavedAt = new Date().getTime();
|
|
261
257
|
_this.dispatchConnectState('saved', lastSavedAt);
|
|
262
258
|
}
|
|
263
|
-
if (type === 'reconnect') {
|
|
264
|
-
_this.disconnect = false;
|
|
265
|
-
}
|
|
266
259
|
_this.eventBus.dispatch(type, message);
|
|
267
260
|
};
|
|
268
261
|
this.closeSocketConnect = function () {
|
|
@@ -271,11 +264,11 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
|
|
|
271
264
|
this.editor = editor;
|
|
272
265
|
this.document = document;
|
|
273
266
|
this.socketClient = new SocketClient(config);
|
|
274
|
-
this.isSending = false;
|
|
275
267
|
this.pendingOperationList = []; // Two-dimensional arrays: [operations, operations, ...]
|
|
276
268
|
this.remoteOperationsList = []; // Same with pending operations
|
|
277
269
|
this.revertOperationList = [];
|
|
278
270
|
this.eventBus = EventBus.getInstance();
|
|
271
|
+
this.state = STATE.IDLE;
|
|
279
272
|
});
|
|
280
273
|
SocketManager.getInstance = function (editor, document, socketConfig) {
|
|
281
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
|
}
|