@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
- var serverVersion = result.version;
29
- var clientVersion = socketManager.getDocumentVersion();
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.receiveOperations = function (params) {
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.receiveOperations(params);
97
+ socketManager.onReceiveRemoteOperations(params);
101
98
  };
102
- this.syncDocumentBySocket = function () {
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.syncDocumentBySocket(result);
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.receiveOperations);
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.addOperations = function (operations) {
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.isSending || _this.pendingOperationList.length === 0) return;
25
- _this.isSending = true;
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.isSending = false;
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(dupSendingOperations);
70
+ _this.pendingOperationList.unshift(_toConsumableArray(_this._sendingOperations));
71
+ _this.state = STATE.CONFLICT;
58
72
  var lose_operations = result.lose_operations;
59
- _this.execLoseOperations(lose_operations);
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
- } else if (error_type === 'Internal_server_error') {
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
- } else if (error_type === 'operation_exec_error') {
68
- var _dupSendingOperations = _toConsumableArray(_this._sendingOperations);
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
- revertOperationList(_this.editor, [_dupSendingOperations]);
89
+ var dupSendingOperations = _toConsumableArray(_this._sendingOperations);
90
+ revertOperationList(_this.editor, [dupSendingOperations]);
72
91
 
73
- // reset control flag
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.execLoseOperations = function (loseOperations) {
82
- // 1. Revert operations
83
- // 1.1 record reverted operationList & clear pendingOperationList
84
- _this.revertOperationList = _this.pendingOperationList.slice();
85
- _this.pendingOperationList = [];
86
-
87
- // 1.2 Revert operationList
88
- debug('revert locale operations: %O', _this.revertOperationList);
89
- revertOperationList(_this.editor, _this.revertOperationList);
90
- _this.editor.isRemote = true;
91
- loseOperations = loseOperations.sort(function (prev, next) {
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
- // 2.2 Update document
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.receiveOperations = function (params) {
127
- _this.remoteOperationsList.push(params);
128
- // sort operations by version
129
- if (_this.remoteOperationsList.length > 1) {
130
- _this.remoteOperationsList = _this.remoteOperationsList.sort(function (prev, next) {
131
- return prev.version - next.version;
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
- // update execute remote operations flag
141
- _this.isExecRemoteOperations = true;
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.execRemoteOperations = function () {
146
- if (_this.remoteOperationsList.length > 0) {
147
- var params = _this.remoteOperationsList.shift();
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.syncDocumentBySocket = function (result) {
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
- _this.remoteOperationsList = content;
230
-
231
- // update execute remote operations flag
232
- _this.isExecRemoteOperations = true;
173
+ var loseOperations = content;
174
+ _this.resolveConflicting(loseOperations);
175
+ };
176
+ this.resolveConflicting = function (loseOperations) {
233
177
  _this.editor.isRemote = true;
234
- _this.execRemoteOperations();
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.isSending = false;
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.addOperations && _socketManager.addOperations(updateOperations);
33
+ _socketManager.onReceiveLocalOperations(updateOperations);
34
34
  }
35
35
  _socketManager.sendCursorLocation(editor.selection);
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.1.118",
3
+ "version": "0.1.119",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",