@seafile/sdoc-editor 0.1.116 → 0.1.118

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.
@@ -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: 0px;
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: 180px;
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: 12px;
43
+ font-size: 14px;
44
44
  color: #212529;
45
- padding-left: 24px;
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
- copy(String(getUrl(element.doc_uuid)), {
23
- format: 'text/plain'
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) {
@@ -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';
@@ -8,6 +9,10 @@ import { deleteCursor } from '../cursor/helper';
8
9
  var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, document, config) {
9
10
  var _this = this;
10
11
  _classCallCheck(this, SocketManager);
12
+ this.getDocumentVersion = function () {
13
+ var version = _this.document.version;
14
+ return version;
15
+ };
11
16
  this.addOperations = function (operations) {
12
17
  _this.pendingOperationList.push(operations);
13
18
  if (_this.pendingOperationList.length > 5) {
@@ -15,10 +20,6 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
15
20
  }
16
21
  _this.sendOperations();
17
22
  };
18
- this.getDocumentVersion = function () {
19
- var version = _this.document.version;
20
- return version;
21
- };
22
23
  this.sendOperations = function () {
23
24
  if (_this.isSending || _this.pendingOperationList.length === 0) return;
24
25
  _this.isSending = true;
@@ -29,14 +30,17 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
29
30
  _this.isSending = false;
30
31
  return;
31
32
  }
33
+ if (_this._sendingOperations || _this.disconnect) return;
32
34
  _this.dispatchConnectState('is-saving');
33
35
  var version = _this.document.version;
34
36
  var operations = _this.pendingOperationList.shift();
35
37
  var selection = _this.editor.selection;
38
+ _this._sendingOperations = operations;
36
39
  _this.socketClient.sendOperations(operations, version, selection, _this.sendOperationsCallback);
37
40
  };
38
41
  this.sendOperationsCallback = function (result) {
39
42
  if (result && result.success) {
43
+ _this._sendingOperations = null;
40
44
  var serverVersion = result.version;
41
45
  _this.document['version'] = serverVersion;
42
46
  var lastSavedAt = new Date().getTime();
@@ -46,18 +50,25 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
46
50
  }
47
51
 
48
52
  // Operations are execute failure
49
- var error_type = result.error_type,
50
- operations = result.operations;
53
+ var error_type = result.error_type;
51
54
  if (error_type === 'version_behind_server') {
55
+ var dupSendingOperations = _toConsumableArray(_this._sendingOperations);
52
56
  // Put the failed operation into the pending list and re-execute it
53
- _this.pendingOperationList.unshift(operations);
54
- _this.sendNextOperations();
57
+ _this.pendingOperationList.unshift(dupSendingOperations);
58
+ var lose_operations = result.lose_operations;
59
+ _this.execLoseOperations(lose_operations);
55
60
  } else if (error_type === 'document_content_load_failed') {
61
+ _this._sendingOperations = null;
56
62
  // After a short-term reconnection, the content of the document fails to load
57
63
  _this.dispatchConnectState(error_type);
64
+ } else if (error_type === 'Internal_server_error') {
65
+ _this._sendingOperations = null;
66
+ _this.dispatchConnectState(error_type);
58
67
  } else if (error_type === 'operation_exec_error') {
68
+ var _dupSendingOperations = _toConsumableArray(_this._sendingOperations);
69
+ _this._sendingOperations = null;
59
70
  _this.editor.isRemote = true;
60
- revertOperationList(_this.editor, [operations]);
71
+ revertOperationList(_this.editor, [_dupSendingOperations]);
61
72
 
62
73
  // reset control flag
63
74
  Promise.resolve().then(function (_) {
@@ -67,6 +78,51 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
67
78
  });
68
79
  }
69
80
  };
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
101
+ debug('execute remote operations: %O', operations);
102
+ syncRemoteOperations(_this.editor, operations);
103
+
104
+ // 2.2 Update document
105
+ _this.document.version = serverVersion;
106
+ _this.document.children = _this.editor.children;
107
+ }
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
+ };
70
126
  this.receiveOperations = function (params) {
71
127
  _this.remoteOperationsList.push(params);
72
128
  // sort operations by version
@@ -74,17 +130,17 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
74
130
  _this.remoteOperationsList = _this.remoteOperationsList.sort(function (prev, next) {
75
131
  return prev.version - next.version;
76
132
  });
133
+ var clientVersion = _this.document.version;
134
+ _this.remoteOperationsList = _this.remoteOperationsList.filter(function (item) {
135
+ return item.version > clientVersion;
136
+ });
77
137
  }
78
- if (_this.isExecRemoteOperations) return;
138
+ if (_this.isExecRemoteOperations || _this._sendingOperations) return;
139
+
140
+ // update execute remote operations flag
79
141
  _this.isExecRemoteOperations = true;
80
142
  _this.editor.isRemote = true;
81
143
  _this.execRemoteOperations();
82
-
83
- // reset control flag
84
- Promise.resolve().then(function (_) {
85
- _this.isExecRemoteOperations = false;
86
- _this.editor.isRemote = false;
87
- });
88
144
  };
89
145
  this.execRemoteOperations = function () {
90
146
  if (_this.remoteOperationsList.length > 0) {
@@ -123,11 +179,14 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
123
179
  // Re-execute undone operations after isRemote is set to false
124
180
  // Need resend this operations to server
125
181
  Promise.resolve().then(function (_) {
126
- debug('Editor isRemote is false: %s', _this.editor.isRemote);
127
- debug('Re-execute pending operations, %O', _this.revertOperationList);
182
+ // reset execute remote operations flag
183
+ _this.isExecRemoteOperations = false;
184
+ _this.editor.isRemote = false;
128
185
 
129
186
  // 3. Execute pending operations
130
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);
131
190
  reExecRevertOperationList(_this.editor, _this.revertOperationList);
132
191
 
133
192
  // 3.2 Clear revert operationList
@@ -168,15 +227,11 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
168
227
  // sync operations
169
228
  // content is [{version, operations}, {version, operations}, ...]
170
229
  _this.remoteOperationsList = content;
230
+
231
+ // update execute remote operations flag
171
232
  _this.isExecRemoteOperations = true;
172
233
  _this.editor.isRemote = true;
173
234
  _this.execRemoteOperations();
174
-
175
- // reset control flag
176
- Promise.resolve().then(function (_) {
177
- _this.isExecRemoteOperations = false;
178
- _this.editor.isRemote = false;
179
- });
180
235
  };
181
236
  this.sendCursorLocation = function (location) {
182
237
  _this.socketClient.sendCursorLocation(location);
@@ -193,6 +248,21 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
193
248
  deleteCursor(_this.editor, message);
194
249
  _this.editor.onCursor && _this.editor.onCursor(_this.editor.cursors);
195
250
  }
251
+ if (type === 'disconnect') {
252
+ if (_this._sendingOperations) {
253
+ _this.pendingOperationList.unshift(_this._sendingOperations.slice());
254
+ _this._sendingOperations = null;
255
+ }
256
+ _this.isSending = false;
257
+ _this.disconnect = true;
258
+
259
+ // Update saved state
260
+ var lastSavedAt = new Date().getTime();
261
+ _this.dispatchConnectState('saved', lastSavedAt);
262
+ }
263
+ if (type === 'reconnect') {
264
+ _this.disconnect = false;
265
+ }
196
266
  _this.eventBus.dispatch(type, message);
197
267
  };
198
268
  this.closeSocketConnect = function () {
@@ -39,6 +39,14 @@ var TipMessage = /*#__PURE__*/function (_React$Component) {
39
39
  duration: null
40
40
  });
41
41
  };
42
+ _this.onOperationsSaveError = function () {
43
+ var t = _this.props.t;
44
+ var message = t('Operations_save_error_tip');
45
+ toaster.danger(message, {
46
+ hasCloseButton: false,
47
+ duration: null
48
+ });
49
+ };
42
50
  _this.onDisconnect = function () {
43
51
  var _this$props = _this.props,
44
52
  t = _this$props.t,
@@ -138,6 +146,7 @@ var TipMessage = /*#__PURE__*/function (_React$Component) {
138
146
  this.unsubscribeOpExecError = eventBus.subscribe('operation_exec_error', this.onOperationExecuteError);
139
147
  this.unsubscribePendingOpExceedLimit = eventBus.subscribe('pending_operations_exceed_limit', this.onPendingOpExceedLimit);
140
148
  this.unsubscribeDocumentLoadError = eventBus.subscribe('document_content_load_failed', this.onDocumentLoadError);
149
+ this.unsubscribeOperationsSaveError = eventBus.subscribe('Internal_server_error', this.onOperationsSaveError);
141
150
  }
142
151
  }, {
143
152
  key: "componentWillUnmount",
@@ -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, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.1.116",
3
+ "version": "0.1.118",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -349,5 +349,6 @@
349
349
  "Icon_and_text_Link": "Icon and text Link",
350
350
  "Card": "Card",
351
351
  "Select_sdoc_document": "Select sdoc document",
352
- "Local_file": "Local file"
352
+ "Local_file": "Local file",
353
+ "Operations_save_error_tip": "Operation execution failed, internal server error, please refresh the page"
353
354
  }
@@ -351,5 +351,6 @@
351
351
  "Icon_and_text_Link": "图标+文字链",
352
352
  "Card": "卡片",
353
353
  "Select_sdoc_document": "选择sdoc文档",
354
- "Local_file": "本地文件"
354
+ "Local_file": "本地文件",
355
+ "Operations_save_error_tip": "操作执行失败,服务器内部错误,请刷新页面"
355
356
  }