@seafile/sdoc-editor 0.2.29 → 0.2.30-beta

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.
Files changed (42) hide show
  1. package/dist/api/seafile-api.js +19 -0
  2. package/dist/basic-sdk/comment/components/comment-item-content.js +9 -1
  3. package/dist/basic-sdk/comment/components/comment-item-reply.js +9 -1
  4. package/dist/basic-sdk/comment/components/comment-item-wrapper.js +1 -1
  5. package/dist/basic-sdk/comment/components/comment-list.css +29 -0
  6. package/dist/basic-sdk/comment/components/comment-list.js +5 -3
  7. package/dist/basic-sdk/comment/components/editor-comment.js +5 -3
  8. package/dist/basic-sdk/comment/components/global-comment/index.js +10 -7
  9. package/dist/basic-sdk/comment/constants/index.js +8 -1
  10. package/dist/basic-sdk/comment/hooks/{use-comment-list.js → comment-hooks/use-comment-list.js} +1 -1
  11. package/dist/basic-sdk/comment/hooks/{use-comment-mount.js → comment-hooks/use-comment-mount.js} +40 -2
  12. package/dist/basic-sdk/comment/hooks/notification-hooks/index.js +2 -0
  13. package/dist/basic-sdk/comment/hooks/notification-hooks/use-notification-context.js +11 -0
  14. package/dist/basic-sdk/comment/hooks/notification-hooks/use-notification-mount.js +85 -0
  15. package/dist/basic-sdk/comment/index.js +46 -7
  16. package/dist/basic-sdk/comment/{comment-context-provider.js → provider/comment-context-provider.js} +3 -3
  17. package/dist/basic-sdk/comment/provider/index.js +8 -0
  18. package/dist/basic-sdk/comment/provider/notification-context-provider.js +26 -0
  19. package/dist/basic-sdk/comment/reducer/notification-reducer.js +70 -0
  20. package/dist/basic-sdk/comment/utils/index.js +2 -1
  21. package/dist/basic-sdk/comment/utils/notification-utils.js +52 -0
  22. package/dist/basic-sdk/constants/index.js +5 -1
  23. package/dist/basic-sdk/editor/editable-article.js +1 -2
  24. package/dist/basic-sdk/socket/socket-client.js +7 -0
  25. package/dist/basic-sdk/socket/socket-manager.js +3 -0
  26. package/dist/components/doc-operations/comments-operation/index.css +14 -0
  27. package/dist/components/doc-operations/comments-operation/index.js +23 -4
  28. package/dist/components/doc-operations/revision-operations/view-changes/index.js +37 -2
  29. package/dist/constants/index.js +2 -1
  30. package/dist/context.js +14 -0
  31. package/dist/model/index.js +2 -1
  32. package/dist/model/notification.js +13 -0
  33. package/package.json +3 -3
  34. package/public/locales/cs/sdoc-editor.json +4 -2
  35. package/public/locales/de/sdoc-editor.json +4 -2
  36. package/public/locales/en/sdoc-editor.json +4 -1
  37. package/public/locales/es/sdoc-editor.json +4 -2
  38. package/public/locales/fr/sdoc-editor.json +4 -2
  39. package/public/locales/it/sdoc-editor.json +4 -2
  40. package/public/locales/ru/sdoc-editor.json +6 -4
  41. package/public/locales/zh_CN/sdoc-editor.json +4 -2
  42. /package/dist/basic-sdk/comment/hooks/{use-comment-context.js → comment-hooks/use-comment-context.js} +0 -0
@@ -242,6 +242,25 @@ var SeafileAPI = /*#__PURE__*/function () {
242
242
  };
243
243
  return this.req.put(url, params);
244
244
  }
245
+
246
+ // notification
247
+ }, {
248
+ key: "listUnseenNotifications",
249
+ value: function listUnseenNotifications(docUuid) {
250
+ var url = "/api/v2.1/seadoc/notifications/".concat(docUuid, "/");
251
+ return this.req.get(url);
252
+ }
253
+ }, {
254
+ key: "deleteUnseenNotifications",
255
+ value: function deleteUnseenNotifications(docUuid, notificationIds) {
256
+ var url = "/api/v2.1/seadoc/notifications/".concat(docUuid, "/");
257
+ var params = {
258
+ ids: notificationIds
259
+ };
260
+ return this.req.delete(url, {
261
+ data: params
262
+ });
263
+ }
245
264
  }]);
246
265
  return SeafileAPI;
247
266
  }();
@@ -7,6 +7,7 @@ import dayjs from 'dayjs';
7
7
  import CommentEditor from './comment-editor';
8
8
  import Tooltip from '../../../components/tooltip';
9
9
  import { textToHtml } from '../utils';
10
+ import { useNotificationContext } from '../hooks/notification-hooks';
10
11
  var CommentItem = function CommentItem(_ref) {
11
12
  var isActive = _ref.isActive,
12
13
  container = _ref.container,
@@ -23,10 +24,13 @@ var CommentItem = function CommentItem(_ref) {
23
24
  _useState4 = _slicedToArray(_useState3, 2),
24
25
  isEditing = _useState4[0],
25
26
  setIsEditing = _useState4[1];
27
+ var _useNotificationConte = useNotificationContext(),
28
+ notificationsInfo = _useNotificationConte.notificationsInfo;
26
29
  var onEditToggle = useCallback(function (event) {
27
30
  event.stopPropagation();
28
31
  setIsEditing(true);
29
32
  }, []);
33
+ var isUnseen = notificationsInfo.notifications_map["sdoc_notification_".concat(comment.id)] ? true : false;
30
34
  var onDeleteToggle = useCallback(function (event) {
31
35
  event.stopPropagation();
32
36
  onDeleteComment(true);
@@ -81,7 +85,11 @@ var CommentItem = function CommentItem(_ref) {
81
85
  className: "name"
82
86
  }, comment.user_name), /*#__PURE__*/React.createElement("span", {
83
87
  className: "time"
84
- }, dayjs(comment.updated_at).format('MM-DD HH:mm')))), isActive && /*#__PURE__*/React.createElement("div", {
88
+ }, dayjs(comment.updated_at).format('MM-DD HH:mm'), isUnseen && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
89
+ className: "sdoc-unread-message-tip"
90
+ }), /*#__PURE__*/React.createElement("span", {
91
+ className: "sdoc-unread-message-text-tip"
92
+ }, t('New')))))), isActive && /*#__PURE__*/React.createElement("div", {
85
93
  className: "d-flex"
86
94
  }, !comment.resolved && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
87
95
  id: "tooltip_".concat(menuId),
@@ -7,6 +7,7 @@ import context from '../../../context';
7
7
  import CommentEditor from './comment-editor';
8
8
  import CommentDeleteShadow from './comment-delete-shadow';
9
9
  import { textToHtml } from '../utils';
10
+ import { useNotificationContext } from '../hooks/notification-hooks';
10
11
  var CommentItemReply = function CommentItemReply(_ref) {
11
12
  var isActive = _ref.isActive,
12
13
  container = _ref.container,
@@ -18,6 +19,9 @@ var CommentItemReply = function CommentItemReply(_ref) {
18
19
  _useState2 = _slicedToArray(_useState, 2),
19
20
  isDropdownOpen = _useState2[0],
20
21
  setDropdownOpen = _useState2[1];
22
+ var _useNotificationConte = useNotificationContext(),
23
+ notificationsInfo = _useNotificationConte.notificationsInfo;
24
+ var isUnseen = notificationsInfo.notifications_map["sdoc_notification_".concat(reply.comment_id, "_").concat(reply.id)] ? true : false;
21
25
  var _useState3 = useState(false),
22
26
  _useState4 = _slicedToArray(_useState3, 2),
23
27
  isEditing = _useState4[0],
@@ -67,7 +71,11 @@ var CommentItemReply = function CommentItemReply(_ref) {
67
71
  className: "name"
68
72
  }, reply.user_name), /*#__PURE__*/React.createElement("span", {
69
73
  className: "time"
70
- }, dayjs(reply.updated_at).format('MM-DD HH:mm')))), isActive && user.username === reply.author && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Dropdown, {
74
+ }, dayjs(reply.updated_at).format('MM-DD HH:mm'), isUnseen && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
75
+ className: "sdoc-unread-message-tip"
76
+ }), /*#__PURE__*/React.createElement("span", {
77
+ className: "sdoc-unread-message-text-tip"
78
+ }, t('New')))))), isActive && user.username === reply.author && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Dropdown, {
71
79
  isOpen: isDropdownOpen,
72
80
  toggle: function toggle() {
73
81
  return setDropdownOpen(!isDropdownOpen);
@@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
6
6
  import dayjs from 'dayjs';
7
7
  import classNames from 'classnames';
8
8
  import context from '../../../context';
9
- import { useCommentContext } from '../hooks/use-comment-context';
9
+ import { useCommentContext } from '../hooks/comment-hooks/use-comment-context';
10
10
  import CommentItemContent from './comment-item-content';
11
11
  import CommentItemReply from './comment-item-reply';
12
12
  import CommentEditor from './comment-editor';
@@ -101,6 +101,35 @@
101
101
  color: #444746;
102
102
  font-size: 12px;
103
103
  line-height: 16px;
104
+ display: inline-flex;
105
+ align-items: center;
106
+ }
107
+
108
+ .sdoc-comment-list-container .comment-header .sdoc-unread-message-tip {
109
+ display: inline-block;
110
+ height: 6px;
111
+ width: 6px;
112
+ border-radius: 50%;
113
+ background-color: #fc6440;
114
+ margin-left: 6px;
115
+ }
116
+
117
+ .sdoc-comment-list-container .comment-header .sdoc-unread-message-text-tip {
118
+ display: none;
119
+ height: 16px;
120
+ padding: 0 5px;
121
+ border-radius: 8px;
122
+ margin-left: 6px;
123
+ background-color: #fc6440;
124
+ color: #fff;
125
+ }
126
+
127
+ .sdoc-comment-list-container .comment-ui-container:hover .comment-header .sdoc-unread-message-tip {
128
+ display: none;
129
+ }
130
+
131
+ .sdoc-comment-list-container .comment-ui-container:hover .comment-header .sdoc-unread-message-text-tip {
132
+ display: inline-block;
104
133
  }
105
134
 
106
135
  .sdoc-comment-list-container .comment-ui-container .comment-operation {
@@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
6
6
  import dayjs from 'dayjs';
7
7
  import context from '../../../context';
8
8
  import { useCommentListPosition } from '../../hooks/use-selection-position';
9
- import { useCommentContext } from '../hooks/use-comment-context';
9
+ import { useCommentContext } from '../hooks/comment-hooks/use-comment-context';
10
10
  import CommentEditor from './comment-editor';
11
11
  import CommentItemWrapper from './comment-item-wrapper';
12
12
  import EventBus from '../../utils/event-bus';
@@ -15,7 +15,8 @@ import './comment-list.css';
15
15
  var CommentList = function CommentList(_ref) {
16
16
  var comments = _ref.comments,
17
17
  selectionElement = _ref.selectionElement,
18
- hiddenComment = _ref.hiddenComment;
18
+ hiddenComment = _ref.hiddenComment,
19
+ deleteUnseenNotifications = _ref.deleteUnseenNotifications;
19
20
  var commentRef = useRef(null);
20
21
  var position = useCommentListPosition();
21
22
  var _useState = useState(false),
@@ -39,7 +40,8 @@ var CommentList = function CommentList(_ref) {
39
40
  var onCommentClick = useCallback(function (comment) {
40
41
  if (activeComment && activeComment.id === comment.id) return;
41
42
  setActiveComment(comment);
42
- }, [activeComment]);
43
+ deleteUnseenNotifications && deleteUnseenNotifications(comment);
44
+ }, [activeComment, deleteUnseenNotifications]);
43
45
  var _useCommentContext = useCommentContext(),
44
46
  dispatch = _useCommentContext.dispatch;
45
47
  var insertComment = useCallback( /*#__PURE__*/function () {
@@ -6,10 +6,11 @@ import useSelectionUpdate from '../../hooks/use-selection-update';
6
6
  import { useCursorPosition } from '../helper';
7
7
  import CommentList from './comment-list';
8
8
  import { useSelectionElement } from '../../hooks/use-selection-element';
9
- import { useCommentContext } from '../hooks/use-comment-context';
9
+ import { useCommentContext } from '../hooks/comment-hooks/use-comment-context';
10
10
  import ElementsCommentCount from './elements-comment-count';
11
11
  import { ELEMENT_TYPE } from '../../extension/constants';
12
- var EditorComment = function EditorComment() {
12
+ var EditorComment = function EditorComment(_ref) {
13
+ var deleteUnseenNotifications = _ref.deleteUnseenNotifications;
13
14
  useSelectionUpdate();
14
15
  var editor = useSlateStatic();
15
16
  var selectionElement = useSelectionElement();
@@ -94,7 +95,8 @@ var EditorComment = function EditorComment() {
94
95
  }, isShowComments && /*#__PURE__*/React.createElement(CommentList, {
95
96
  comments: comments,
96
97
  selectionElement: selectionElement,
97
- hiddenComment: hiddenComment
98
+ hiddenComment: hiddenComment,
99
+ deleteUnseenNotifications: deleteUnseenNotifications
98
100
  }))));
99
101
  };
100
102
  export default EditorComment;
@@ -6,16 +6,18 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
6
6
  import dayjs from 'dayjs';
7
7
  import { ElementPopover } from '../../../extension/commons';
8
8
  import EventBus from '../../../utils/event-bus';
9
- import useCommentList from '../../hooks/use-comment-list';
9
+ import useCommentList from '../../hooks/comment-hooks/use-comment-list';
10
10
  import CommentItemWrapper from '../comment-item-wrapper';
11
11
  import GlobalCommentHeader from './global-comment-header';
12
12
  import GlobalCommentBodyHeader from './global-comment-body-header';
13
13
  import { DOC_COMMENT_ELEMENT_ID } from '../../constants';
14
14
  import context from '../../../../context';
15
- import { useCommentContext } from '../../hooks/use-comment-context';
15
+ import { useCommentContext } from '../../hooks/comment-hooks/use-comment-context';
16
16
  import GlobalCommentEditor from './global-comment-editor';
17
+ import { INTERNAL_EVENT } from '../../../constants';
17
18
  import './index.css';
18
- var GlobalComment = function GlobalComment() {
19
+ var GlobalComment = function GlobalComment(_ref) {
20
+ var deleteUnseenNotifications = _ref.deleteUnseenNotifications;
19
21
  var _useState = useState(null),
20
22
  _useState2 = _slicedToArray(_useState, 2),
21
23
  activeComment = _useState2[0],
@@ -58,7 +60,7 @@ var GlobalComment = function GlobalComment() {
58
60
  }, [isShowCommentList, toggle]);
59
61
  useEffect(function () {
60
62
  var eventBus = EventBus.getInstance();
61
- var unsubscribe = eventBus.subscribe('COMMENT_LIST_CLICK', toggle);
63
+ var unsubscribe = eventBus.subscribe(INTERNAL_EVENT.COMMENT_LIST_CLICK, toggle);
62
64
  return function () {
63
65
  unsubscribe();
64
66
  };
@@ -76,11 +78,12 @@ var GlobalComment = function GlobalComment() {
76
78
  var onCommentClick = useCallback(function (comment) {
77
79
  if (activeComment && activeComment.id === comment.id) return;
78
80
  setActiveComment(comment);
79
- }, [activeComment]);
81
+ deleteUnseenNotifications && deleteUnseenNotifications(comment);
82
+ }, [activeComment, deleteUnseenNotifications]);
80
83
  var _useCommentContext = useCommentContext(),
81
84
  dispatch = _useCommentContext.dispatch;
82
85
  var insertComment = useCallback( /*#__PURE__*/function () {
83
- var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(elementId, comment) {
86
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(elementId, comment) {
84
87
  var res, returnComment, newComment;
85
88
  return _regeneratorRuntime().wrap(function _callee$(_context) {
86
89
  while (1) switch (_context.prev = _context.next) {
@@ -111,7 +114,7 @@ var GlobalComment = function GlobalComment() {
111
114
  }, _callee);
112
115
  }));
113
116
  return function (_x, _x2) {
114
- return _ref.apply(this, arguments);
117
+ return _ref2.apply(this, arguments);
115
118
  };
116
119
  }(), [dispatch]);
117
120
  var insertDocComment = useCallback(function (commentDetail) {
@@ -10,4 +10,11 @@ export var FONT_SIZE_WIDTH = 15;
10
10
  export var LINE_HEIGHT = 22;
11
11
  export var POPOVER_ADDING_HEIGHT = 10;
12
12
  export var COMMENT_URL_CLASSNAME = 'sdoc-comment-url';
13
- export var DOC_COMMENT_ELEMENT_ID = '0';
13
+ export var DOC_COMMENT_ELEMENT_ID = '0';
14
+ export var DOC_NOTIFICATION_REDUCER_TYPE = {
15
+ FETCHING: 'fetching',
16
+ FETCHED: 'fetched',
17
+ FETCH_ERROR: 'error',
18
+ ADD: 'add',
19
+ DEL: 'del'
20
+ };
@@ -1,7 +1,7 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
2
2
  import { useEffect, useState } from 'react';
3
3
  import { useCommentContext } from './use-comment-context';
4
- import { COMMENT_TYPES, DOC_COMMENT_ELEMENT_ID } from '../constants';
4
+ import { COMMENT_TYPES, DOC_COMMENT_ELEMENT_ID } from '../../constants';
5
5
  var useCommentList = function useCommentList() {
6
6
  var _useCommentContext = useCommentContext(),
7
7
  commentsInfo = _useCommentContext.commentsInfo;
@@ -1,7 +1,9 @@
1
1
  import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
2
2
  function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(typeof e + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
3
3
  import { useCallback, useEffect } from 'react';
4
- import context from '../../../context';
4
+ import context from '../../../../context';
5
+ import EventBus from '../../../utils/event-bus';
6
+ import { EXTERNAL_EVENT } from '../../../../constants';
5
7
  export var useCommentsMount = function useCommentsMount(dispatch) {
6
8
  var request = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
7
9
  var res, comments;
@@ -36,7 +38,43 @@ export var useCommentsMount = function useCommentsMount(dispatch) {
36
38
  }
37
39
  }, _callee, null, [[1, 9]]);
38
40
  })), []);
41
+ var reRequest = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
42
+ var res, comments;
43
+ return _regeneratorRuntime().wrap(function _callee2$(_context2) {
44
+ while (1) switch (_context2.prev = _context2.next) {
45
+ case 0:
46
+ dispatch({
47
+ type: 'REFETCHING_STATE'
48
+ });
49
+ _context2.prev = 1;
50
+ _context2.next = 4;
51
+ return context.listComments();
52
+ case 4:
53
+ res = _context2.sent;
54
+ comments = res.data.comments;
55
+ dispatch({
56
+ type: 'RECEIVE_STATE',
57
+ payload: comments
58
+ });
59
+ _context2.next = 12;
60
+ break;
61
+ case 9:
62
+ _context2.prev = 9;
63
+ _context2.t0 = _context2["catch"](1);
64
+ console.log(_context2.t0);
65
+ // dispatch({type: 'FETCHING_ERROR'});
66
+ case 12:
67
+ case "end":
68
+ return _context2.stop();
69
+ }
70
+ }, _callee2, null, [[1, 9]]);
71
+ })), []);
39
72
  useEffect(function () {
40
73
  request();
41
- }, [request]);
74
+ var eventBus = EventBus.getInstance();
75
+ var unsubscribeNewNotification = eventBus.subscribe(EXTERNAL_EVENT.NEW_NOTIFICATION, reRequest);
76
+ return function () {
77
+ unsubscribeNewNotification();
78
+ };
79
+ }, [request, reRequest]);
42
80
  };
@@ -0,0 +1,2 @@
1
+ export { useNotificationContext, NotificationContext } from './use-notification-context';
2
+ export { useNotificationsMount } from './use-notification-mount';
@@ -0,0 +1,11 @@
1
+ import React, { useContext } from 'react';
2
+ export var NotificationContext = React.createContext();
3
+ export var useNotificationContext = function useNotificationContext() {
4
+ var _useContext = useContext(NotificationContext),
5
+ notificationsInfo = _useContext.notificationsInfo,
6
+ dispatch = _useContext.dispatch;
7
+ return {
8
+ notificationsInfo: notificationsInfo,
9
+ dispatch: dispatch
10
+ };
11
+ };
@@ -0,0 +1,85 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
2
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(typeof e + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
3
+ import { useCallback, useEffect } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import context from '../../../../context';
6
+ import EventBus from '../../../utils/event-bus';
7
+ import { EXTERNAL_EVENT } from '../../../../constants';
8
+ import { DOC_NOTIFICATION_REDUCER_TYPE } from '../../constants';
9
+ import { createNotify } from '../../utils';
10
+ import { useCollaborators } from '../../../../hooks';
11
+ import { INTERNAL_EVENT } from '../../../constants';
12
+ export var useNotificationsMount = function useNotificationsMount(dispatch) {
13
+ var _useTranslation = useTranslation(),
14
+ t = _useTranslation.t;
15
+ var _useCollaborators = useCollaborators(),
16
+ collaborators = _useCollaborators.collaborators;
17
+ var popupBrowserCommentNotification = useCallback(function (notification) {
18
+ if (!notification) return;
19
+ var author = notification.author,
20
+ msgType = notification.msg_type,
21
+ reply = notification.reply,
22
+ comment = notification.comment;
23
+ var authorInfo = collaborators.find(function (collaborator) {
24
+ return collaborator.email === author;
25
+ });
26
+ var notificationContent = comment || reply;
27
+ var titleKey = msgType === 'comment' ? 'xxx_added_a_new_comment' : 'xxx_added_a_reply';
28
+ var title = t(titleKey, {
29
+ author: authorInfo ? authorInfo.name : t('Unknown')
30
+ });
31
+ var options = {
32
+ body: "".concat(notificationContent)
33
+ };
34
+ createNotify(title, options);
35
+ }, [collaborators, t]);
36
+ var request = useCallback( /*#__PURE__*/function () {
37
+ var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(notification) {
38
+ var eventBus, res, notifications;
39
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
40
+ while (1) switch (_context.prev = _context.next) {
41
+ case 0:
42
+ popupBrowserCommentNotification(notification);
43
+ eventBus = EventBus.getInstance();
44
+ dispatch({
45
+ type: DOC_NOTIFICATION_REDUCER_TYPE.FETCHING
46
+ });
47
+ _context.prev = 3;
48
+ _context.next = 6;
49
+ return context.listUnseenNotifications();
50
+ case 6:
51
+ res = _context.sent;
52
+ notifications = res.data.notifications;
53
+ dispatch({
54
+ type: DOC_NOTIFICATION_REDUCER_TYPE.FETCHED,
55
+ payload: notifications
56
+ });
57
+ eventBus.dispatch(INTERNAL_EVENT.UNSEEN_NOTIFICATIONS_COUNT, notifications === null || notifications === void 0 ? void 0 : notifications.length);
58
+ _context.next = 16;
59
+ break;
60
+ case 12:
61
+ _context.prev = 12;
62
+ _context.t0 = _context["catch"](3);
63
+ console.log(_context.t0);
64
+ dispatch({
65
+ type: DOC_NOTIFICATION_REDUCER_TYPE.FETCH_ERROR
66
+ });
67
+ case 16:
68
+ case "end":
69
+ return _context.stop();
70
+ }
71
+ }, _callee, null, [[3, 12]]);
72
+ }));
73
+ return function (_x) {
74
+ return _ref.apply(this, arguments);
75
+ };
76
+ }(), [popupBrowserCommentNotification]);
77
+ useEffect(function () {
78
+ request();
79
+ var eventBus = EventBus.getInstance();
80
+ var unsubscribeNewNotification = eventBus.subscribe(EXTERNAL_EVENT.NEW_NOTIFICATION, request);
81
+ return function () {
82
+ unsubscribeNewNotification();
83
+ };
84
+ }, [request]);
85
+ };
@@ -1,13 +1,52 @@
1
- import React from 'react';
2
- import { useCommentContext } from './hooks/use-comment-context';
1
+ import React, { useCallback } from 'react';
2
+ import { useCommentContext } from './hooks/comment-hooks/use-comment-context';
3
+ import { useNotificationContext } from './hooks/notification-hooks';
3
4
  import { EditorComment, GlobalComment } from './components';
4
5
  import { ParticipantsProvider } from './hooks/use-participants';
5
- var CommentWrapper = function CommentWrapper() {
6
+ import Provider from './provider';
7
+ import { generatorNotificationKey } from './utils';
8
+ import context from '../../context';
9
+ import { DOC_NOTIFICATION_REDUCER_TYPE } from './constants';
10
+ var CommentWrapperContainer = function CommentWrapperContainer() {
6
11
  var _useCommentContext = useCommentContext(),
7
12
  commentsInfo = _useCommentContext.commentsInfo;
8
- if (commentsInfo.isFetching) {
9
- return null;
10
- }
11
- return /*#__PURE__*/React.createElement(ParticipantsProvider, null, /*#__PURE__*/React.createElement(EditorComment, null), /*#__PURE__*/React.createElement(GlobalComment, null));
13
+ var _useNotificationConte = useNotificationContext(),
14
+ notificationsInfo = _useNotificationConte.notificationsInfo,
15
+ notificationDispatch = _useNotificationConte.dispatch;
16
+ var deleteUnseenNotifications = useCallback(function (comment) {
17
+ var unseenCommentIds = [];
18
+ var unseenNotificationKeys = [];
19
+ var commentNotificationKey = generatorNotificationKey(comment.id);
20
+ var commentNotification = notificationsInfo.notifications_map[commentNotificationKey];
21
+ if (commentNotification) {
22
+ unseenNotificationKeys.push(commentNotification.key);
23
+ unseenCommentIds.push(commentNotification.id);
24
+ }
25
+ Array.isArray(comment.replies) && comment.replies.forEach(function (reply) {
26
+ var replyNotificationKey = generatorNotificationKey(reply.comment_id, reply.id);
27
+ var replyNotification = notificationsInfo.notifications_map[replyNotificationKey];
28
+ if (replyNotification) {
29
+ unseenNotificationKeys.push(replyNotification.key);
30
+ unseenCommentIds.push(replyNotification.id);
31
+ }
32
+ });
33
+ context.deleteUnseenNotifications(unseenCommentIds).then(function (res) {
34
+ notificationDispatch({
35
+ type: DOC_NOTIFICATION_REDUCER_TYPE.DEL,
36
+ payload: unseenNotificationKeys
37
+ });
38
+ }).catch(function (error) {
39
+ //
40
+ });
41
+ }, [notificationDispatch, notificationsInfo.notifications_map]);
42
+ if (commentsInfo.isFetching) return null;
43
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(EditorComment, {
44
+ deleteUnseenNotifications: deleteUnseenNotifications
45
+ }), /*#__PURE__*/React.createElement(GlobalComment, {
46
+ deleteUnseenNotifications: deleteUnseenNotifications
47
+ }));
48
+ };
49
+ var CommentWrapper = function CommentWrapper() {
50
+ return /*#__PURE__*/React.createElement(Provider, null, /*#__PURE__*/React.createElement(ParticipantsProvider, null, /*#__PURE__*/React.createElement(CommentWrapperContainer, null)));
12
51
  };
13
52
  export default CommentWrapper;
@@ -1,10 +1,10 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
3
3
  import React, { useEffect, useReducer } from 'react';
4
- import { commentReducer, initCommentsInfo } from './reducer/comment-reducer';
5
- import { useCommentsMount } from './hooks/use-comment-mount';
6
- import { CommentContext } from './hooks/use-comment-context';
7
4
  import { useSlateStatic } from '@seafile/slate-react';
5
+ import { commentReducer, initCommentsInfo } from '../reducer/comment-reducer';
6
+ import { useCommentsMount } from '../hooks/comment-hooks/use-comment-mount';
7
+ import { CommentContext } from '../hooks/comment-hooks/use-comment-context';
8
8
  var CommentContextProvider = function CommentContextProvider(_ref) {
9
9
  var children = _ref.children;
10
10
  var _useReducer = useReducer(commentReducer, initCommentsInfo),
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import CommentContextProvider from './comment-context-provider';
3
+ import NotificationContextProvider from './notification-context-provider';
4
+ var Provider = function Provider(_ref) {
5
+ var children = _ref.children;
6
+ return /*#__PURE__*/React.createElement(NotificationContextProvider, null, /*#__PURE__*/React.createElement(CommentContextProvider, null, children));
7
+ };
8
+ export default Provider;
@@ -0,0 +1,26 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
3
+ import React, { useEffect, useReducer } from 'react';
4
+ import { useSlateStatic } from '@seafile/slate-react';
5
+ import { notificationReducer, initNotificationsInfo } from '../reducer/notification-reducer';
6
+ import { useNotificationsMount, NotificationContext } from '../hooks/notification-hooks';
7
+ var NotificationContextProvider = function NotificationContextProvider(_ref) {
8
+ var children = _ref.children;
9
+ var _useReducer = useReducer(notificationReducer, initNotificationsInfo),
10
+ _useReducer2 = _slicedToArray(_useReducer, 2),
11
+ notificationsInfo = _useReducer2[0],
12
+ dispatch = _useReducer2[1];
13
+ useNotificationsMount(dispatch);
14
+ var editor = useSlateStatic();
15
+ useEffect(function () {
16
+ editor.notifications_map = _objectSpread({}, notificationsInfo.notifications_map);
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ }, [notificationsInfo]);
19
+ return /*#__PURE__*/React.createElement(NotificationContext.Provider, {
20
+ value: {
21
+ notificationsInfo: notificationsInfo,
22
+ dispatch: dispatch
23
+ }
24
+ }, children);
25
+ };
26
+ export default NotificationContextProvider;
@@ -0,0 +1,70 @@
1
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
3
+ import { DOC_NOTIFICATION_REDUCER_TYPE } from '../constants';
4
+ import { Notification } from '../../../model';
5
+ import { EventBus } from '../../';
6
+ import { INTERNAL_EVENT } from '../../constants';
7
+ export var initNotificationsInfo = {
8
+ isFetching: true,
9
+ notifications_map: {},
10
+ error: false
11
+ };
12
+ export var notificationReducer = function notificationReducer(state, action) {
13
+ switch (action.type) {
14
+ case DOC_NOTIFICATION_REDUCER_TYPE.FETCHING:
15
+ {
16
+ return initNotificationsInfo;
17
+ }
18
+ case DOC_NOTIFICATION_REDUCER_TYPE.FETCHED:
19
+ {
20
+ var notifications = action.payload;
21
+ var notificationsMap = {};
22
+ notifications.forEach(function (n) {
23
+ var newNotification = new Notification(n);
24
+ notificationsMap[newNotification.key] = newNotification;
25
+ });
26
+ return {
27
+ isFetching: false,
28
+ notifications_map: notificationsMap,
29
+ error: false
30
+ };
31
+ }
32
+ case DOC_NOTIFICATION_REDUCER_TYPE.FETCH_ERROR:
33
+ {
34
+ return {
35
+ isFetching: false,
36
+ notifications_map: {},
37
+ error: true
38
+ };
39
+ }
40
+ case DOC_NOTIFICATION_REDUCER_TYPE.ADD:
41
+ {
42
+ var notification = action.payload.notification;
43
+ var newNotification = new Notification(notification);
44
+ return _objectSpread(_objectSpread({}, state), {}, {
45
+ notifications_map: _objectSpread(_objectSpread({}, state.notifications_map), {}, _defineProperty({}, newNotification.key, newNotification))
46
+ });
47
+ }
48
+ case DOC_NOTIFICATION_REDUCER_TYPE.DEL:
49
+ {
50
+ var notificationKeys = action.payload;
51
+ var notifications_map = state.notifications_map;
52
+ if (Array.isArray(notificationKeys) && notificationKeys.length > 0) {
53
+ notificationKeys.forEach(function (notificationKey) {
54
+ if (notifications_map[notificationKey]) {
55
+ delete notifications_map[notificationKey];
56
+ }
57
+ });
58
+ }
59
+ var eventBus = EventBus.getInstance();
60
+ eventBus.dispatch(INTERNAL_EVENT.UNSEEN_NOTIFICATIONS_COUNT, Object.keys(notifications_map).length);
61
+ return _objectSpread(_objectSpread({}, state), {}, {
62
+ notifications_map: notifications_map
63
+ });
64
+ }
65
+ default:
66
+ {
67
+ return state;
68
+ }
69
+ }
70
+ };
@@ -3,6 +3,7 @@ import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
3
3
  import { getEventTransfer } from '../../../utils';
4
4
  import { COMMENT_URL_CLASSNAME } from '../constants';
5
5
  import { KeyCodes } from '../../../constants';
6
+ import { createNotify, generatorNotificationKey } from './notification-utils';
6
7
  export var searchCollaborators = function searchCollaborators(collaborators, searchValue) {
7
8
  var validSearchValue = searchValue ? searchValue.trim().toLowerCase() : '';
8
9
  var validCollaborators = Array.isArray(collaborators) && collaborators.length > 0 ? collaborators : [];
@@ -203,4 +204,4 @@ var CommentUtilities = /*#__PURE__*/_createClass(function CommentUtilities() {
203
204
  }
204
205
  };
205
206
  });
206
- export { CommentUtilities };
207
+ export { CommentUtilities, createNotify, generatorNotificationKey };
@@ -0,0 +1,52 @@
1
+ import dayjs from 'dayjs';
2
+ import relativeTime from 'dayjs/plugin/relativeTime';
3
+ import context from '../../../context';
4
+ dayjs.extend(relativeTime);
5
+ var PERMISSION_GRANTED = 'granted';
6
+ var notify = function notify($title, $options) {
7
+ var notification = new Notification($title, $options);
8
+
9
+ //auto clear notifications
10
+ var timer = setTimeout(notification.close.bind(notification), 5000);
11
+ notification.onshow = function (event) {
12
+ var _context$getSetting = context.getSetting('mediaUrl'),
13
+ mediaUrl = _context$getSetting.mediaUrl;
14
+ var newAudioElement = document.createElement('audio');
15
+ newAudioElement.setAttribute('src', "".concat(mediaUrl, "audio/classic.mp3"));
16
+ newAudioElement.setAttribute('autoplay', 'autoplay');
17
+ newAudioElement.setAttribute('id', 'seatable-audio');
18
+ var audioElement = document.getElementById('seatable-audio');
19
+ if (audioElement) {
20
+ document.body.removeChild(audioElement);
21
+ }
22
+ document.body.appendChild(newAudioElement);
23
+ };
24
+ notification.onclose = function () {
25
+ clearTimeout(timer);
26
+ };
27
+ notification.onclick = function () {
28
+ notification.close();
29
+ };
30
+ };
31
+ export var createNotify = function createNotify(title, options) {
32
+ // Let's check if the browser supports notifications
33
+ if (!('Notification' in window)) {
34
+ return false;
35
+ }
36
+ if (Notification.permission === PERMISSION_GRANTED) {
37
+ notify(title, options);
38
+ } else {
39
+ Notification.requestPermission(function (res) {
40
+ if (res === PERMISSION_GRANTED) {
41
+ notify(title, options);
42
+ }
43
+ });
44
+ }
45
+ };
46
+ export var generatorNotificationKey = function generatorNotificationKey(commentId) {
47
+ var replyId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
48
+ var validCommentId = commentId + '';
49
+ var validReplyId = replyId + '';
50
+ if (!validReplyId) return "sdoc_notification_".concat(validCommentId);
51
+ return "sdoc_notification_".concat(validCommentId, "_").concat(validReplyId);
52
+ };
@@ -7,8 +7,12 @@ export var INTERNAL_EVENT = {
7
7
  OUTLINE_STATE_CHANGED: 'outline_state_changed',
8
8
  RELOAD_IMAGE: 'reload_image',
9
9
  ARTICLE_CLICK: 'hidden_comment',
10
- UPDATE_TAG_VIEW: 'update_tag_view'
10
+ UPDATE_TAG_VIEW: 'update_tag_view',
11
+ COMMENT_LIST_CLICK: 'comment_list_click',
12
+ UNSEEN_NOTIFICATIONS_COUNT: 'unseen_notifications_count'
11
13
  };
14
+ export var REVISION_DIFF_KEY = 'diff';
15
+ export var REVISION_DIFF_VALUE = '1';
12
16
  export var PAGE_EDIT_AREA_WIDTH = 672; // 672 = 794 - 2[borderLeft + borderRight] - 120[paddingLeft + paddingRight]
13
17
 
14
18
  export var MODIFY_TYPE = {
@@ -5,7 +5,6 @@ import { getAboveBlockNode, getNextNode, getPrevNode, isSelectionAtBlockEnd, isS
5
5
  import EventProxy from '../utils/event-handler';
6
6
  import { useCursors } from '../cursor/use-cursors';
7
7
  import { INTERNAL_EVENT } from '../constants';
8
- import CommentContextProvider from '../comment/comment-context-provider';
9
8
  import CommentWrapper from '../comment';
10
9
  import { usePipDecorate } from '../decorates';
11
10
  import { getCursorPosition, getDomHeight, getDomMarginTop } from '../utils/dom-utils';
@@ -142,7 +141,7 @@ var EditableArticle = function EditableArticle(_ref) {
142
141
  decorate: decorate,
143
142
  onCut: eventProxy.onCut,
144
143
  onCopy: eventProxy.onCopy
145
- })), /*#__PURE__*/React.createElement(SideToolbar, null), isShowComment && /*#__PURE__*/React.createElement(CommentContextProvider, null, /*#__PURE__*/React.createElement(CommentWrapper, null))));
144
+ })), /*#__PURE__*/React.createElement(SideToolbar, null), isShowComment && /*#__PURE__*/React.createElement(CommentWrapper, null)));
146
145
  };
147
146
  EditableArticle.defaultProps = {
148
147
  isShowComment: true
@@ -143,6 +143,10 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
143
143
  var socketManager = SocketManager.getInstance();
144
144
  socketManager.receiveRemoveDocumentError();
145
145
  };
146
+ this.receiveNewNotification = function (notification) {
147
+ var socketManager = SocketManager.getInstance();
148
+ socketManager.receiveNewNotification(notification);
149
+ };
146
150
  this.config = config;
147
151
  this.isReconnect = false;
148
152
  this.socket = io(config.sdocServer, {
@@ -170,6 +174,9 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
170
174
  this.socket.on('doc-removed', this.receiveRemoveDocument);
171
175
  this.socket.on('doc-removed-error', this.receiveRemoveDocumentError);
172
176
  this.socket.on('update-cursor', this.receiveCursorLocation);
177
+
178
+ // notification
179
+ this.socket.on('new-notification', this.receiveNewNotification);
173
180
  this.socket.io.on('reconnect', this.onReconnect);
174
181
  this.socket.io.on('reconnect_attempt', this.onReconnectAttempt);
175
182
  this.socket.io.on('reconnect_error', this.onReconnectError);
@@ -48,6 +48,9 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, _do
48
48
  this.receiveDocumentReplacedError = function () {
49
49
  _this.eventBus.dispatch(EXTERNAL_EVENT.DOCUMENT_REPLACED_ERROR);
50
50
  };
51
+ this.receiveNewNotification = function (notification) {
52
+ _this.eventBus.dispatch(EXTERNAL_EVENT.NEW_NOTIFICATION, notification);
53
+ };
51
54
  this.onReceiveLocalOperations = function (operations) {
52
55
  _this.pendingOperationList.push(operations);
53
56
  var lastOpBeginTime = new Date().getTime();
@@ -0,0 +1,14 @@
1
+ .sdoc-notification-container .sdoc-notification-count {
2
+ position: absolute;
3
+ color: #fff;
4
+ font-size: 12px;
5
+ top: -7px;
6
+ right: -10px;
7
+ padding: 2px;
8
+ background: #fc6440;
9
+ border-radius: 50%;
10
+ min-width: 20px;
11
+ min-height: 20px;
12
+ transform: scale(.7);
13
+ text-align: center;
14
+ }
@@ -1,16 +1,35 @@
1
- import React, { useCallback } from 'react';
1
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
2
+ import React, { useCallback, useEffect, useState } from 'react';
2
3
  import { EventBus } from '../../../basic-sdk';
3
4
  import { eventStopPropagation } from '../../../basic-sdk/utils/mouse-event';
5
+ import { INTERNAL_EVENT } from '../../../basic-sdk/constants';
6
+ import './index.css';
4
7
  export default function CommentsOperation() {
8
+ var _useState = useState(0),
9
+ _useState2 = _slicedToArray(_useState, 2),
10
+ unseenNotificationsCount = _useState2[0],
11
+ setUnseenNotificationsCount = _useState2[1];
5
12
  var onCommentsToggle = useCallback(function (event) {
6
13
  eventStopPropagation(event);
7
14
  var eventBus = EventBus.getInstance();
8
- eventBus.dispatch('COMMENT_LIST_CLICK');
15
+ eventBus.dispatch(INTERNAL_EVENT.COMMENT_LIST_CLICK);
16
+ }, []);
17
+ useEffect(function () {
18
+ var eventBus = EventBus.getInstance();
19
+ var unsubscribeUnseenNotificationsCount = eventBus.subscribe(INTERNAL_EVENT.UNSEEN_NOTIFICATIONS_COUNT, function (count) {
20
+ return setUnseenNotificationsCount(count);
21
+ });
22
+ return function () {
23
+ unsubscribeUnseenNotificationsCount();
24
+ };
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9
26
  }, []);
10
27
  return /*#__PURE__*/React.createElement("span", {
11
- className: "op-item",
28
+ className: "op-item sdoc-notification-container",
12
29
  onClick: onCommentsToggle
13
30
  }, /*#__PURE__*/React.createElement("i", {
14
31
  className: "sdocfont sdoc-comments"
15
- }));
32
+ }), unseenNotificationsCount > 0 && /*#__PURE__*/React.createElement("span", {
33
+ className: "sdoc-notification-count"
34
+ }, unseenNotificationsCount));
16
35
  }
@@ -1,15 +1,50 @@
1
- import React, { useCallback } from 'react';
1
+ import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
2
+ import React, { useCallback, useEffect } from 'react';
2
3
  import { useTranslation } from 'react-i18next';
3
4
  import Switch from '../../../switch';
4
5
  import Tooltip from '../../../tooltip';
6
+ import { REVISION_DIFF_KEY, REVISION_DIFF_VALUE } from '../../../../basic-sdk/constants';
5
7
  import './index.css';
6
8
  var ViewChanges = function ViewChanges(_ref) {
7
9
  var isShowChanges = _ref.isShowChanges,
8
10
  propsOnViewChangesToggle = _ref.onViewChangesToggle;
9
11
  var _useTranslation = useTranslation(),
10
12
  t = _useTranslation.t;
13
+ useEffect(function () {
14
+ var url = new URL(window.location.href);
15
+ var searchParams = new URLSearchParams(url.search);
16
+ if (!searchParams.has(REVISION_DIFF_KEY)) return;
17
+ var firstLoadValue = searchParams.get(REVISION_DIFF_KEY);
18
+ if (firstLoadValue === REVISION_DIFF_VALUE) {
19
+ propsOnViewChangesToggle(true);
20
+ }
21
+ // eslint-disable-next-line react-hooks/exhaustive-deps
22
+ }, []);
11
23
  var onViewChangesToggle = useCallback(function () {
12
- propsOnViewChangesToggle(!isShowChanges);
24
+ var nextIsShowChanges = !isShowChanges;
25
+ var url = new URL(window.location.href);
26
+ var searchParams = new URLSearchParams(url.search);
27
+ var newParamsString = '';
28
+ var _iterator = _createForOfIteratorHelper(searchParams.entries()),
29
+ _step;
30
+ try {
31
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
32
+ var item = _step.value;
33
+ if (item[0] !== REVISION_DIFF_KEY) {
34
+ newParamsString = newParamsString + "&".concat(item[0], "=").concat(item[1]);
35
+ }
36
+ }
37
+ } catch (err) {
38
+ _iterator.e(err);
39
+ } finally {
40
+ _iterator.f();
41
+ }
42
+ if (!searchParams.has(REVISION_DIFF_KEY) && nextIsShowChanges) {
43
+ newParamsString = newParamsString + "&".concat(REVISION_DIFF_KEY, "=").concat(REVISION_DIFF_VALUE);
44
+ }
45
+ var newURL = "".concat(url.origin).concat(url.pathname).concat(newParamsString ? '/?' + newParamsString : '');
46
+ window.history.replaceState(null, null, newURL);
47
+ propsOnViewChangesToggle(nextIsShowChanges);
13
48
  // eslint-disable-next-line react-hooks/exhaustive-deps
14
49
  }, [isShowChanges, propsOnViewChangesToggle]);
15
50
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
@@ -12,7 +12,8 @@ export var EXTERNAL_EVENT = {
12
12
  DOCUMENT_REPLACED: 'document_replaced',
13
13
  DOCUMENT_REPLACED_ERROR: 'document_replaced_error',
14
14
  REMOVE_DOCUMENT: 'remove_document',
15
- REMOVE_DOCUMENT_ERROR: 'remove_document_error'
15
+ REMOVE_DOCUMENT_ERROR: 'remove_document_error',
16
+ NEW_NOTIFICATION: 'new_notification'
16
17
  };
17
18
  export var TIP_TYPE = {
18
19
  DELETE_NO_CHANGES_REVISION: 'delete_no_changes_revision',
package/dist/context.js CHANGED
@@ -344,6 +344,20 @@ var Context = /*#__PURE__*/function () {
344
344
  var docUuid = this.getDocUuid();
345
345
  return this.api.updateRepoTag(docUuid, repoTagId, repoTagName, color);
346
346
  }
347
+
348
+ // notification
349
+ }, {
350
+ key: "listUnseenNotifications",
351
+ value: function listUnseenNotifications() {
352
+ var docUuid = this.getDocUuid();
353
+ return this.api.listUnseenNotifications(docUuid);
354
+ }
355
+ }, {
356
+ key: "deleteUnseenNotifications",
357
+ value: function deleteUnseenNotifications(notificationIds) {
358
+ var docUuid = this.getDocUuid();
359
+ return this.api.deleteUnseenNotifications(docUuid, notificationIds);
360
+ }
347
361
  }]);
348
362
  return Context;
349
363
  }();
@@ -1,3 +1,4 @@
1
1
  import Revision from './revision';
2
2
  import User from './user';
3
- export { Revision, User };
3
+ import Notification from './notification';
4
+ export { Revision, User, Notification };
@@ -0,0 +1,13 @@
1
+ import _createClass from "@babel/runtime/helpers/esm/createClass";
2
+ import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
3
+ import { generatorNotificationKey } from '../basic-sdk/comment/utils';
4
+ var Notification = /*#__PURE__*/_createClass(function Notification(options) {
5
+ var _options$detail, _options$detail2, _options$detail3;
6
+ _classCallCheck(this, Notification);
7
+ this.id = options.id || '';
8
+ this.comment_id = (options === null || options === void 0 ? void 0 : (_options$detail = options.detail) === null || _options$detail === void 0 ? void 0 : _options$detail.comment_id) || '';
9
+ this.reply_id = (options === null || options === void 0 ? void 0 : (_options$detail2 = options.detail) === null || _options$detail2 === void 0 ? void 0 : _options$detail2.reply_id) || '';
10
+ this.type = (options === null || options === void 0 ? void 0 : (_options$detail3 = options.detail) === null || _options$detail3 === void 0 ? void 0 : _options$detail3.msg_type) || '';
11
+ this.key = this.type !== 'reply' ? generatorNotificationKey(this.comment_id) : generatorNotificationKey(this.comment_id, this.reply_id);
12
+ });
13
+ export default Notification;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.2.29",
3
+ "version": "0.2.30beta",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -10,7 +10,7 @@
10
10
  "@seafile/slate-history": "0.86.2",
11
11
  "@seafile/slate-hyperscript": "0.81.7",
12
12
  "@seafile/slate-react": "0.92.6",
13
- "axios": "1.4.0",
13
+ "axios": "^1.6.0",
14
14
  "classnames": "2.3.2",
15
15
  "copy-to-clipboard": "^3.3.3",
16
16
  "dayjs": "1.11.2",
@@ -115,7 +115,7 @@
115
115
  "resolve": "1.12.0",
116
116
  "resolve-url-loader": "3.1.5",
117
117
  "sass-loader": "13.3.2",
118
- "seafile-js": "0.2.202",
118
+ "seafile-js": "0.2.216",
119
119
  "style-loader": "^1.0.1",
120
120
  "terser-webpack-plugin": "4.2.3",
121
121
  "ts-pnp": "1.1.4",
@@ -384,7 +384,6 @@
384
384
  "Merge_tip": "There are conflicts in the documents. Do you want to merge them?",
385
385
  "Has_conflict_before_view_changes_tip": "There are conflicts in the document. Please resolve the conflict before viewing the changes!",
386
386
  "Has_been_removed_tip": "Document has been removed, please view other documents.",
387
- "Revision_created": "Revision {{id}} created",
388
387
  "Checking": "Checking...",
389
388
  "Publishing": "Publishing...",
390
389
  "Inline": "Inline",
@@ -408,5 +407,8 @@
408
407
  "Add_option": "Vytvořit nový štítek",
409
408
  "Find_an_option": "Search tags",
410
409
  "Copy_link_of_section": "Copy link of section",
411
- "revision": "revision"
410
+ "revision": "revision",
411
+ "xxx_added_a_new_comment": "{{author}} added a new comment",
412
+ "xxx_added_a_reply": "{{author}} added a reply",
413
+ "New": "Nový"
412
414
  }
@@ -384,7 +384,6 @@
384
384
  "Merge_tip": "There are conflicts in the documents. Do you want to merge them?",
385
385
  "Has_conflict_before_view_changes_tip": "There are conflicts in the document. Please resolve the conflict before viewing the changes!",
386
386
  "Has_been_removed_tip": "Document has been removed, please view other documents.",
387
- "Revision_created": "Revision {{id}} created",
388
387
  "Checking": "Checking...",
389
388
  "Publishing": "Publishing...",
390
389
  "Inline": "Inline",
@@ -408,5 +407,8 @@
408
407
  "Add_option": "Neues Tag erstellen",
409
408
  "Find_an_option": "Search tags",
410
409
  "Copy_link_of_section": "Copy link of section",
411
- "revision": "revision"
410
+ "revision": "revision",
411
+ "xxx_added_a_new_comment": "{{author}} added a new comment",
412
+ "xxx_added_a_reply": "{{author}} added a reply",
413
+ "New": "Erstellen"
412
414
  }
@@ -407,5 +407,8 @@
407
407
  "Add_option": "Create a new tag",
408
408
  "Find_an_option": "Search tags",
409
409
  "Copy_link_of_section": "Copy link of section",
410
- "revision": "revision"
410
+ "revision": "revision",
411
+ "xxx_added_a_new_comment": "{{author}} added a new comment",
412
+ "xxx_added_a_reply": "{{author}} added a reply",
413
+ "New": "New"
411
414
  }
@@ -384,7 +384,6 @@
384
384
  "Merge_tip": "There are conflicts in the documents. Do you want to merge them?",
385
385
  "Has_conflict_before_view_changes_tip": "There are conflicts in the document. Please resolve the conflict before viewing the changes!",
386
386
  "Has_been_removed_tip": "Document has been removed, please view other documents.",
387
- "Revision_created": "Revision {{id}} created",
388
387
  "Checking": "Checking...",
389
388
  "Publishing": "Publishing...",
390
389
  "Inline": "Inline",
@@ -408,5 +407,8 @@
408
407
  "Add_option": "Cear una nueva etiqueta",
409
408
  "Find_an_option": "Search tags",
410
409
  "Copy_link_of_section": "Copy link of section",
411
- "revision": "revision"
410
+ "revision": "revision",
411
+ "xxx_added_a_new_comment": "{{author}} added a new comment",
412
+ "xxx_added_a_reply": "{{author}} added a reply",
413
+ "New": "Nuevo"
412
414
  }
@@ -384,7 +384,6 @@
384
384
  "Merge_tip": "There are conflicts in the documents. Do you want to merge them?",
385
385
  "Has_conflict_before_view_changes_tip": "There are conflicts in the document. Please resolve the conflict before viewing the changes!",
386
386
  "Has_been_removed_tip": "Document has been removed, please view other documents.",
387
- "Revision_created": "Revision {{id}} created",
388
387
  "Checking": "Checking...",
389
388
  "Publishing": "Publishing...",
390
389
  "Inline": "Inline",
@@ -408,5 +407,8 @@
408
407
  "Add_option": "Créer un nouveau Tag",
409
408
  "Find_an_option": "Search tags",
410
409
  "Copy_link_of_section": "Copy link of section",
411
- "revision": "revision"
410
+ "revision": "revision",
411
+ "xxx_added_a_new_comment": "{{author}} added a new comment",
412
+ "xxx_added_a_reply": "{{author}} added a reply",
413
+ "New": "Créer"
412
414
  }
@@ -384,7 +384,6 @@
384
384
  "Merge_tip": "There are conflicts in the documents. Do you want to merge them?",
385
385
  "Has_conflict_before_view_changes_tip": "There are conflicts in the document. Please resolve the conflict before viewing the changes!",
386
386
  "Has_been_removed_tip": "Document has been removed, please view other documents.",
387
- "Revision_created": "Revision {{id}} created",
388
387
  "Checking": "Checking...",
389
388
  "Publishing": "Publishing...",
390
389
  "Inline": "Inline",
@@ -408,5 +407,8 @@
408
407
  "Add_option": "Crea un nuovo tag",
409
408
  "Find_an_option": "Search tags",
410
409
  "Copy_link_of_section": "Copy link of section",
411
- "revision": "revision"
410
+ "revision": "revision",
411
+ "xxx_added_a_new_comment": "{{author}} added a new comment",
412
+ "xxx_added_a_reply": "{{author}} added a reply",
413
+ "New": "Nuovo"
412
414
  }
@@ -378,13 +378,12 @@
378
378
  "Keep_both_modification": "Сохранить обе модификации",
379
379
  "Tip": "Совет",
380
380
  "Rebase_delete_no_change_revision_tip": "Редакция не внесла никаких изменений по сравнению с исходным документом. Вы хотите удалить эту редакцию?",
381
- "Has_been_replaced_tip": "Document content has been replaced. Please refresh the page",
382
- "Has_been_published_tip": "Revision published",
381
+ "Has_been_replaced_tip": "Содержимое документа было заменено. Обновите страницу",
382
+ "Has_been_published_tip": "Версия опубликована",
383
383
  "Has_conflict_before_publish_tip": "В документе есть конфликты, разрешите их перед публикацией!",
384
384
  "Merge_tip": "В документах есть конфликты. Вы хотите объединить их?",
385
385
  "Has_conflict_before_view_changes_tip": "В документе есть конфликты. Разрешите конфликт перед просмотром изменений!",
386
386
  "Has_been_removed_tip": "Документ удален, посмотрите другие документы.",
387
- "Revision_created": "Версия {{id}} создана",
388
387
  "Checking": "Проверка...",
389
388
  "Publishing": "Публикация...",
390
389
  "Inline": "Встроенный",
@@ -408,5 +407,8 @@
408
407
  "Add_option": "Создать новый тег",
409
408
  "Find_an_option": "Поиск тегов",
410
409
  "Copy_link_of_section": "Копировать ссылку на раздел",
411
- "revision": "revision"
410
+ "revision": "версия",
411
+ "xxx_added_a_new_comment": "{{author}} добавил новый комментарий",
412
+ "xxx_added_a_reply": "{{author}} добавил ответ",
413
+ "New": "Новый"
412
414
  }
@@ -384,7 +384,6 @@
384
384
  "Merge_tip": "文档有冲突,是否合并?",
385
385
  "Has_conflict_before_view_changes_tip": "文档有冲突,请解决冲突后再查看更改!",
386
386
  "Has_been_removed_tip": "文档已经被删除,请查看其他文档",
387
- "Revision_created": "已创建修订稿 {{id}}",
388
387
  "Checking": "检查中...",
389
388
  "Publishing": "发布中...",
390
389
  "Inline": "嵌入行内",
@@ -408,5 +407,8 @@
408
407
  "Add_option": "新建标签",
409
408
  "Find_an_option": "查找标签",
410
409
  "Copy_link_of_section": "拷贝章节的链接",
411
- "revision": "修订稿"
410
+ "revision": "修订稿",
411
+ "xxx_added_a_new_comment": "{{author}} 添加了一条新评论",
412
+ "xxx_added_a_reply": "{{author}} 添加了一条回复",
413
+ "New": "新"
412
414
  }