@atlaskit/editor-plugin-emoji 3.6.6 → 3.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlaskit/editor-plugin-emoji
2
2
 
3
+ ## 3.6.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#154163](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/154163)
8
+ [`82b017a2d9588`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/82b017a2d9588) -
9
+ Add error handling if offline editing to refresh the emoji node when returning online.
10
+
3
11
  ## 3.6.6
4
12
 
5
13
  ### Patch Changes
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.EmojiNodeView = void 0;
8
+ exports.isSingleEmoji = isSingleEmoji;
8
9
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
11
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
@@ -14,15 +15,47 @@ var _coreUtils = require("@atlaskit/editor-common/core-utils");
14
15
  var _emoji = require("@atlaskit/editor-common/emoji");
15
16
  var _monitoring = require("@atlaskit/editor-common/monitoring");
16
17
  var _model = require("@atlaskit/editor-prosemirror/model");
18
+ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
17
19
  var _emojiNodeSpec = require("./emojiNodeSpec");
20
+ /**
21
+ * Check if we can nicely fallback to the nodes text
22
+ *
23
+ * @param fallbackText string of the nodes fallback text
24
+ *
25
+ * @example
26
+ * isSingleEmoji('😀') // true
27
+ */
28
+ function isSingleEmoji(fallbackText) {
29
+ // Regular expression to match a single emoji character
30
+ var emojiRegex = /^((?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDDFF\uDE70-\uDE7C\uDE80-\uDE89\uDE8F-\uDEC6\uDECE-\uDEDC\uDEDF-\uDEE9\uDEF0-\uDEF8])|(?:[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u2388\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2605\u2607-\u2612\u2614-\u2685\u2690-\u2705\u2708-\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2767\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC00-\uDCFF\uDD0D-\uDD0F\uDD2F\uDD6C-\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDAD-\uDDE5\uDE01-\uDE0F\uDE1A\uDE2F\uDE32-\uDE3A\uDE3C-\uDE3F\uDE49-\uDFFA]|\uD83D[\uDC00-\uDD3D\uDD46-\uDE4F\uDE80-\uDEFF\uDF74-\uDF7F\uDFD5-\uDFFF]|\uD83E[\uDC0C-\uDC0F\uDC48-\uDC4F\uDC5A-\uDC5F\uDC88-\uDC8F\uDCAE-\uDCFF\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDEFF]|\uD83F[\uDC00-\uDFFD])\uFE0F?(?:\u200D(?:[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u2388\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2605\u2607-\u2612\u2614-\u2685\u2690-\u2705\u2708-\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2767\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC00-\uDCFF\uDD0D-\uDD0F\uDD2F\uDD6C-\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDAD-\uDDE5\uDE01-\uDE0F\uDE1A\uDE2F\uDE32-\uDE3A\uDE3C-\uDE3F\uDE49-\uDFFA]|\uD83D[\uDC00-\uDD3D\uDD46-\uDE4F\uDE80-\uDEFF\uDF74-\uDF7F\uDFD5-\uDFFF]|\uD83E[\uDC0C-\uDC0F\uDC48-\uDC4F\uDC5A-\uDC5F\uDC88-\uDC8F\uDCAE-\uDCFF\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDEFF]|\uD83F[\uDC00-\uDFFD])\uFE0F?)+|(?:\uD83C[\uDDE6-\uDDFF])(?:\uD83C[\uDDE6-\uDDFF]))$/;
31
+ return emojiRegex.test(fallbackText);
32
+ }
33
+
34
+ /**
35
+ * Emoji node view for renderering emoji nodes
36
+ */
18
37
  var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
38
+ /**
39
+ * Prosemirror node view for rendering emoji nodes. This class is responsible for
40
+ * rendering emoji nodes in the editor, handling updates, and managing fallback rendering.
41
+ *
42
+ * @param node - The ProseMirror node representing the emoji.
43
+ * @param extraProps - An object containing additional parameters.
44
+ * @param extraProps.intl - The internationalization object for formatting messages.
45
+ * @param extraProps.api - The editor API for accessing shared state and connectivity features.
46
+ *
47
+ * @example
48
+ * const emojiNodeView = new EmojiNodeView(node, { intl, api });
49
+ */
19
50
  function EmojiNodeView(node, _ref) {
20
51
  var _api$emoji,
21
52
  _sharedState$currentS,
22
- _this = this;
53
+ _this = this,
54
+ _api$connectivity;
23
55
  var intl = _ref.intl,
24
56
  api = _ref.api;
25
57
  (0, _classCallCheck2.default)(this, EmojiNodeView);
58
+ (0, _defineProperty2.default)(this, "renderingFallback", false);
26
59
  (0, _defineProperty2.default)(this, "destroy", function () {});
27
60
  this.node = node;
28
61
  this.intl = intl;
@@ -57,8 +90,19 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
57
90
  emojiProvider = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider;
58
91
  void _this.updateDom(emojiProvider);
59
92
  });
93
+
94
+ // Refresh emojis if we go back online
95
+ var subscribeToConnection = api === null || api === void 0 || (_api$connectivity = api.connectivity) === null || _api$connectivity === void 0 ? void 0 : _api$connectivity.sharedState.onChange(function (_ref3) {
96
+ var prevSharedState = _ref3.prevSharedState,
97
+ nextSharedState = _ref3.nextSharedState;
98
+ if ((prevSharedState === null || prevSharedState === void 0 ? void 0 : prevSharedState.mode) === 'offline' && (nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode) === 'online' && _this.renderingFallback && (0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true)) {
99
+ var _sharedState$currentS2;
100
+ _this.updateDom((_sharedState$currentS2 = sharedState.currentState()) === null || _sharedState$currentS2 === void 0 ? void 0 : _sharedState$currentS2.emojiProvider);
101
+ }
102
+ });
60
103
  this.destroy = function () {
61
104
  unsubscribe();
105
+ subscribeToConnection === null || subscribeToConnection === void 0 || subscribeToConnection();
62
106
  };
63
107
  }
64
108
  return (0, _createClass2.default)(EmojiNodeView, [{
@@ -136,6 +180,7 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
136
180
  }, {
137
181
  key: "renderFallback",
138
182
  value: function renderFallback() {
183
+ this.renderingFallback = true;
139
184
  this.cleanUpAndRenderCommonAttributes();
140
185
  var fallbackElement = document.createElement('span');
141
186
  fallbackElement.innerText = this.node.attrs.text || this.node.attrs.shortName;
@@ -146,6 +191,7 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
146
191
  }, {
147
192
  key: "renderEmoji",
148
193
  value: function renderEmoji(description, representation) {
194
+ this.renderingFallback = false;
149
195
  this.cleanUpAndRenderCommonAttributes();
150
196
  var emojiType = 'sprite' in representation ? 'sprite' : 'image';
151
197
 
@@ -180,6 +226,7 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
180
226
  }, {
181
227
  key: "createImageEmojiElement",
182
228
  value: function createImageEmojiElement(emojiDescription, representation) {
229
+ var _this2 = this;
183
230
  var imageElement = document.createElement('img');
184
231
  imageElement.classList.add(_emoji.EmojiSharedCssClassName.EMOJI_IMAGE);
185
232
  imageElement.src = 'imagePath' in representation ? representation.imagePath : representation.mediaPath;
@@ -190,6 +237,18 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
190
237
  // Because img.width is round to the nearest integer.
191
238
  imageElement.setAttribute('width', "".concat(_emoji.defaultEmojiHeight / representation.height * representation.width));
192
239
  }
240
+ if ((0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true)) {
241
+ // If there's an error (ie. offline) render the ascii fallback if possible, otherwise
242
+ // mark the node to refresh when returning online.
243
+ imageElement.onerror = function () {
244
+ // Create a check that confirms if this.node.attrs.text if an ascii emoji
245
+ if (isSingleEmoji(_this2.node.attrs.text)) {
246
+ _this2.renderFallback();
247
+ } else {
248
+ _this2.renderingFallback = true;
249
+ }
250
+ };
251
+ }
193
252
  return imageElement;
194
253
  }
195
254
  }], [{
@@ -3,18 +3,50 @@ import { isSSR } from '@atlaskit/editor-common/core-utils';
3
3
  import { messages, EmojiSharedCssClassName, defaultEmojiHeight } from '@atlaskit/editor-common/emoji';
4
4
  import { logException } from '@atlaskit/editor-common/monitoring';
5
5
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
6
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
7
  import { emojiToDom } from './emojiNodeSpec';
8
+ /**
9
+ * Check if we can nicely fallback to the nodes text
10
+ *
11
+ * @param fallbackText string of the nodes fallback text
12
+ *
13
+ * @example
14
+ * isSingleEmoji('😀') // true
15
+ */
16
+ export function isSingleEmoji(fallbackText) {
17
+ // Regular expression to match a single emoji character
18
+ const emojiRegex = /^(\p{Emoji_Presentation}|\p{Extended_Pictographic}\u{FE0F}?(?:\u{200D}\p{Extended_Pictographic}\u{FE0F}?)+|\p{Regional_Indicator}\p{Regional_Indicator})$/u;
19
+ return emojiRegex.test(fallbackText);
20
+ }
21
+
22
+ /**
23
+ * Emoji node view for renderering emoji nodes
24
+ */
7
25
  export class EmojiNodeView {
8
26
  static logError(error) {
9
27
  void logException(error, {
10
28
  location: 'editor-plugin-emoji/EmojiNodeView'
11
29
  });
12
30
  }
31
+
32
+ /**
33
+ * Prosemirror node view for rendering emoji nodes. This class is responsible for
34
+ * rendering emoji nodes in the editor, handling updates, and managing fallback rendering.
35
+ *
36
+ * @param node - The ProseMirror node representing the emoji.
37
+ * @param extraProps - An object containing additional parameters.
38
+ * @param extraProps.intl - The internationalization object for formatting messages.
39
+ * @param extraProps.api - The editor API for accessing shared state and connectivity features.
40
+ *
41
+ * @example
42
+ * const emojiNodeView = new EmojiNodeView(node, { intl, api });
43
+ */
13
44
  constructor(node, {
14
45
  intl,
15
46
  api
16
47
  }) {
17
- var _api$emoji, _sharedState$currentS;
48
+ var _api$emoji, _sharedState$currentS, _api$connectivity;
49
+ _defineProperty(this, "renderingFallback", false);
18
50
  _defineProperty(this, "destroy", () => {});
19
51
  this.node = node;
20
52
  this.intl = intl;
@@ -51,8 +83,20 @@ export class EmojiNodeView {
51
83
  emojiProvider = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider;
52
84
  void this.updateDom(emojiProvider);
53
85
  });
86
+
87
+ // Refresh emojis if we go back online
88
+ const subscribeToConnection = api === null || api === void 0 ? void 0 : (_api$connectivity = api.connectivity) === null || _api$connectivity === void 0 ? void 0 : _api$connectivity.sharedState.onChange(({
89
+ prevSharedState,
90
+ nextSharedState
91
+ }) => {
92
+ if ((prevSharedState === null || prevSharedState === void 0 ? void 0 : prevSharedState.mode) === 'offline' && (nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode) === 'online' && this.renderingFallback && editorExperiment('platform_editor_offline_editing_web', true)) {
93
+ var _sharedState$currentS2;
94
+ this.updateDom((_sharedState$currentS2 = sharedState.currentState()) === null || _sharedState$currentS2 === void 0 ? void 0 : _sharedState$currentS2.emojiProvider);
95
+ }
96
+ });
54
97
  this.destroy = () => {
55
98
  unsubscribe();
99
+ subscribeToConnection === null || subscribeToConnection === void 0 ? void 0 : subscribeToConnection();
56
100
  };
57
101
  }
58
102
  async updateDom(emojiProvider) {
@@ -104,6 +148,7 @@ export class EmojiNodeView {
104
148
  }
105
149
  }
106
150
  renderFallback() {
151
+ this.renderingFallback = true;
107
152
  this.cleanUpAndRenderCommonAttributes();
108
153
  const fallbackElement = document.createElement('span');
109
154
  fallbackElement.innerText = this.node.attrs.text || this.node.attrs.shortName;
@@ -112,6 +157,7 @@ export class EmojiNodeView {
112
157
  this.dom.appendChild(fallbackElement);
113
158
  }
114
159
  renderEmoji(description, representation) {
160
+ this.renderingFallback = false;
115
161
  this.cleanUpAndRenderCommonAttributes();
116
162
  const emojiType = 'sprite' in representation ? 'sprite' : 'image';
117
163
 
@@ -152,6 +198,18 @@ export class EmojiNodeView {
152
198
  // Because img.width is round to the nearest integer.
153
199
  imageElement.setAttribute('width', `${defaultEmojiHeight / representation.height * representation.width}`);
154
200
  }
201
+ if (editorExperiment('platform_editor_offline_editing_web', true)) {
202
+ // If there's an error (ie. offline) render the ascii fallback if possible, otherwise
203
+ // mark the node to refresh when returning online.
204
+ imageElement.onerror = () => {
205
+ // Create a check that confirms if this.node.attrs.text if an ascii emoji
206
+ if (isSingleEmoji(this.node.attrs.text)) {
207
+ this.renderFallback();
208
+ } else {
209
+ this.renderingFallback = true;
210
+ }
211
+ };
212
+ }
155
213
  return imageElement;
156
214
  }
157
215
  }
@@ -7,15 +7,47 @@ import { isSSR } from '@atlaskit/editor-common/core-utils';
7
7
  import { messages, EmojiSharedCssClassName, defaultEmojiHeight } from '@atlaskit/editor-common/emoji';
8
8
  import { logException } from '@atlaskit/editor-common/monitoring';
9
9
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
10
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
10
11
  import { emojiToDom } from './emojiNodeSpec';
12
+ /**
13
+ * Check if we can nicely fallback to the nodes text
14
+ *
15
+ * @param fallbackText string of the nodes fallback text
16
+ *
17
+ * @example
18
+ * isSingleEmoji('😀') // true
19
+ */
20
+ export function isSingleEmoji(fallbackText) {
21
+ // Regular expression to match a single emoji character
22
+ var emojiRegex = /^((?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDDFF\uDE70-\uDE7C\uDE80-\uDE89\uDE8F-\uDEC6\uDECE-\uDEDC\uDEDF-\uDEE9\uDEF0-\uDEF8])|(?:[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u2388\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2605\u2607-\u2612\u2614-\u2685\u2690-\u2705\u2708-\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2767\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC00-\uDCFF\uDD0D-\uDD0F\uDD2F\uDD6C-\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDAD-\uDDE5\uDE01-\uDE0F\uDE1A\uDE2F\uDE32-\uDE3A\uDE3C-\uDE3F\uDE49-\uDFFA]|\uD83D[\uDC00-\uDD3D\uDD46-\uDE4F\uDE80-\uDEFF\uDF74-\uDF7F\uDFD5-\uDFFF]|\uD83E[\uDC0C-\uDC0F\uDC48-\uDC4F\uDC5A-\uDC5F\uDC88-\uDC8F\uDCAE-\uDCFF\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDEFF]|\uD83F[\uDC00-\uDFFD])\uFE0F?(?:\u200D(?:[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u2388\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2605\u2607-\u2612\u2614-\u2685\u2690-\u2705\u2708-\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2767\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC00-\uDCFF\uDD0D-\uDD0F\uDD2F\uDD6C-\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDAD-\uDDE5\uDE01-\uDE0F\uDE1A\uDE2F\uDE32-\uDE3A\uDE3C-\uDE3F\uDE49-\uDFFA]|\uD83D[\uDC00-\uDD3D\uDD46-\uDE4F\uDE80-\uDEFF\uDF74-\uDF7F\uDFD5-\uDFFF]|\uD83E[\uDC0C-\uDC0F\uDC48-\uDC4F\uDC5A-\uDC5F\uDC88-\uDC8F\uDCAE-\uDCFF\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDEFF]|\uD83F[\uDC00-\uDFFD])\uFE0F?)+|(?:\uD83C[\uDDE6-\uDDFF])(?:\uD83C[\uDDE6-\uDDFF]))$/;
23
+ return emojiRegex.test(fallbackText);
24
+ }
25
+
26
+ /**
27
+ * Emoji node view for renderering emoji nodes
28
+ */
11
29
  export var EmojiNodeView = /*#__PURE__*/function () {
30
+ /**
31
+ * Prosemirror node view for rendering emoji nodes. This class is responsible for
32
+ * rendering emoji nodes in the editor, handling updates, and managing fallback rendering.
33
+ *
34
+ * @param node - The ProseMirror node representing the emoji.
35
+ * @param extraProps - An object containing additional parameters.
36
+ * @param extraProps.intl - The internationalization object for formatting messages.
37
+ * @param extraProps.api - The editor API for accessing shared state and connectivity features.
38
+ *
39
+ * @example
40
+ * const emojiNodeView = new EmojiNodeView(node, { intl, api });
41
+ */
12
42
  function EmojiNodeView(node, _ref) {
13
43
  var _api$emoji,
14
44
  _sharedState$currentS,
15
- _this = this;
45
+ _this = this,
46
+ _api$connectivity;
16
47
  var intl = _ref.intl,
17
48
  api = _ref.api;
18
49
  _classCallCheck(this, EmojiNodeView);
50
+ _defineProperty(this, "renderingFallback", false);
19
51
  _defineProperty(this, "destroy", function () {});
20
52
  this.node = node;
21
53
  this.intl = intl;
@@ -50,8 +82,19 @@ export var EmojiNodeView = /*#__PURE__*/function () {
50
82
  emojiProvider = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider;
51
83
  void _this.updateDom(emojiProvider);
52
84
  });
85
+
86
+ // Refresh emojis if we go back online
87
+ var subscribeToConnection = api === null || api === void 0 || (_api$connectivity = api.connectivity) === null || _api$connectivity === void 0 ? void 0 : _api$connectivity.sharedState.onChange(function (_ref3) {
88
+ var prevSharedState = _ref3.prevSharedState,
89
+ nextSharedState = _ref3.nextSharedState;
90
+ if ((prevSharedState === null || prevSharedState === void 0 ? void 0 : prevSharedState.mode) === 'offline' && (nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode) === 'online' && _this.renderingFallback && editorExperiment('platform_editor_offline_editing_web', true)) {
91
+ var _sharedState$currentS2;
92
+ _this.updateDom((_sharedState$currentS2 = sharedState.currentState()) === null || _sharedState$currentS2 === void 0 ? void 0 : _sharedState$currentS2.emojiProvider);
93
+ }
94
+ });
53
95
  this.destroy = function () {
54
96
  unsubscribe();
97
+ subscribeToConnection === null || subscribeToConnection === void 0 || subscribeToConnection();
55
98
  };
56
99
  }
57
100
  return _createClass(EmojiNodeView, [{
@@ -129,6 +172,7 @@ export var EmojiNodeView = /*#__PURE__*/function () {
129
172
  }, {
130
173
  key: "renderFallback",
131
174
  value: function renderFallback() {
175
+ this.renderingFallback = true;
132
176
  this.cleanUpAndRenderCommonAttributes();
133
177
  var fallbackElement = document.createElement('span');
134
178
  fallbackElement.innerText = this.node.attrs.text || this.node.attrs.shortName;
@@ -139,6 +183,7 @@ export var EmojiNodeView = /*#__PURE__*/function () {
139
183
  }, {
140
184
  key: "renderEmoji",
141
185
  value: function renderEmoji(description, representation) {
186
+ this.renderingFallback = false;
142
187
  this.cleanUpAndRenderCommonAttributes();
143
188
  var emojiType = 'sprite' in representation ? 'sprite' : 'image';
144
189
 
@@ -173,6 +218,7 @@ export var EmojiNodeView = /*#__PURE__*/function () {
173
218
  }, {
174
219
  key: "createImageEmojiElement",
175
220
  value: function createImageEmojiElement(emojiDescription, representation) {
221
+ var _this2 = this;
176
222
  var imageElement = document.createElement('img');
177
223
  imageElement.classList.add(EmojiSharedCssClassName.EMOJI_IMAGE);
178
224
  imageElement.src = 'imagePath' in representation ? representation.imagePath : representation.mediaPath;
@@ -183,6 +229,18 @@ export var EmojiNodeView = /*#__PURE__*/function () {
183
229
  // Because img.width is round to the nearest integer.
184
230
  imageElement.setAttribute('width', "".concat(defaultEmojiHeight / representation.height * representation.width));
185
231
  }
232
+ if (editorExperiment('platform_editor_offline_editing_web', true)) {
233
+ // If there's an error (ie. offline) render the ascii fallback if possible, otherwise
234
+ // mark the node to refresh when returning online.
235
+ imageElement.onerror = function () {
236
+ // Create a check that confirms if this.node.attrs.text if an ascii emoji
237
+ if (isSingleEmoji(_this2.node.attrs.text)) {
238
+ _this2.renderFallback();
239
+ } else {
240
+ _this2.renderingFallback = true;
241
+ }
242
+ };
243
+ }
186
244
  return imageElement;
187
245
  }
188
246
  }], [{
@@ -3,6 +3,7 @@ import type { Command, EditorCommand, NextEditorPlugin, OptionalPlugin, TypeAhea
3
3
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
4
4
  import type { InlineCommentInputMethod, InlineCommentMap } from '@atlaskit/editor-plugin-annotation';
5
5
  import type { BasePlugin } from '@atlaskit/editor-plugin-base';
6
+ import type { ConnectivityPlugin } from '@atlaskit/editor-plugin-connectivity';
6
7
  import { type EditorViewModePluginState } from '@atlaskit/editor-plugin-editor-viewmode';
7
8
  import type { MetricsPlugin } from '@atlaskit/editor-plugin-metrics';
8
9
  import type { TypeAheadInputMethod, TypeAheadPlugin } from '@atlaskit/editor-plugin-type-ahead';
@@ -53,7 +54,8 @@ export type EmojiPluginDependencies = [
53
54
  OptionalPlugin<AnnotationPluginType>,
54
55
  OptionalPlugin<EditorViewModePluginType>,
55
56
  OptionalPlugin<BasePlugin>,
56
- OptionalPlugin<MetricsPlugin>
57
+ OptionalPlugin<MetricsPlugin>,
58
+ OptionalPlugin<ConnectivityPlugin>
57
59
  ];
58
60
  export type EmojiPlugin = NextEditorPlugin<'emoji', {
59
61
  pluginConfiguration: EmojiPluginOptions | undefined;
@@ -7,13 +7,38 @@ interface Params {
7
7
  intl: IntlShape;
8
8
  api: ExtractInjectionAPI<EmojiPlugin> | undefined;
9
9
  }
10
+ /**
11
+ * Check if we can nicely fallback to the nodes text
12
+ *
13
+ * @param fallbackText string of the nodes fallback text
14
+ *
15
+ * @example
16
+ * isSingleEmoji('😀') // true
17
+ */
18
+ export declare function isSingleEmoji(fallbackText: string): boolean;
19
+ /**
20
+ * Emoji node view for renderering emoji nodes
21
+ */
10
22
  export declare class EmojiNodeView implements NodeView {
11
23
  dom: Node;
12
24
  domElement: HTMLElement | undefined;
13
25
  private readonly node;
14
26
  private readonly intl;
27
+ private renderingFallback;
15
28
  readonly destroy: () => void;
16
29
  private static logError;
30
+ /**
31
+ * Prosemirror node view for rendering emoji nodes. This class is responsible for
32
+ * rendering emoji nodes in the editor, handling updates, and managing fallback rendering.
33
+ *
34
+ * @param node - The ProseMirror node representing the emoji.
35
+ * @param extraProps - An object containing additional parameters.
36
+ * @param extraProps.intl - The internationalization object for formatting messages.
37
+ * @param extraProps.api - The editor API for accessing shared state and connectivity features.
38
+ *
39
+ * @example
40
+ * const emojiNodeView = new EmojiNodeView(node, { intl, api });
41
+ */
17
42
  constructor(node: PMNode, { intl, api }: Params);
18
43
  private updateDom;
19
44
  private static isEmojiRepresentationSupported;
@@ -3,6 +3,7 @@ import type { Command, EditorCommand, NextEditorPlugin, OptionalPlugin, TypeAhea
3
3
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
4
4
  import type { InlineCommentInputMethod, InlineCommentMap } from '@atlaskit/editor-plugin-annotation';
5
5
  import type { BasePlugin } from '@atlaskit/editor-plugin-base';
6
+ import type { ConnectivityPlugin } from '@atlaskit/editor-plugin-connectivity';
6
7
  import { type EditorViewModePluginState } from '@atlaskit/editor-plugin-editor-viewmode';
7
8
  import type { MetricsPlugin } from '@atlaskit/editor-plugin-metrics';
8
9
  import type { TypeAheadInputMethod, TypeAheadPlugin } from '@atlaskit/editor-plugin-type-ahead';
@@ -53,7 +54,8 @@ export type EmojiPluginDependencies = [
53
54
  OptionalPlugin<AnnotationPluginType>,
54
55
  OptionalPlugin<EditorViewModePluginType>,
55
56
  OptionalPlugin<BasePlugin>,
56
- OptionalPlugin<MetricsPlugin>
57
+ OptionalPlugin<MetricsPlugin>,
58
+ OptionalPlugin<ConnectivityPlugin>
57
59
  ];
58
60
  export type EmojiPlugin = NextEditorPlugin<'emoji', {
59
61
  pluginConfiguration: EmojiPluginOptions | undefined;
@@ -7,13 +7,38 @@ interface Params {
7
7
  intl: IntlShape;
8
8
  api: ExtractInjectionAPI<EmojiPlugin> | undefined;
9
9
  }
10
+ /**
11
+ * Check if we can nicely fallback to the nodes text
12
+ *
13
+ * @param fallbackText string of the nodes fallback text
14
+ *
15
+ * @example
16
+ * isSingleEmoji('😀') // true
17
+ */
18
+ export declare function isSingleEmoji(fallbackText: string): boolean;
19
+ /**
20
+ * Emoji node view for renderering emoji nodes
21
+ */
10
22
  export declare class EmojiNodeView implements NodeView {
11
23
  dom: Node;
12
24
  domElement: HTMLElement | undefined;
13
25
  private readonly node;
14
26
  private readonly intl;
27
+ private renderingFallback;
15
28
  readonly destroy: () => void;
16
29
  private static logError;
30
+ /**
31
+ * Prosemirror node view for rendering emoji nodes. This class is responsible for
32
+ * rendering emoji nodes in the editor, handling updates, and managing fallback rendering.
33
+ *
34
+ * @param node - The ProseMirror node representing the emoji.
35
+ * @param extraProps - An object containing additional parameters.
36
+ * @param extraProps.intl - The internationalization object for formatting messages.
37
+ * @param extraProps.api - The editor API for accessing shared state and connectivity features.
38
+ *
39
+ * @example
40
+ * const emojiNodeView = new EmojiNodeView(node, { intl, api });
41
+ */
17
42
  constructor(node: PMNode, { intl, api }: Params);
18
43
  private updateDom;
19
44
  private static isEmojiRepresentationSupported;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-emoji",
3
- "version": "3.6.6",
3
+ "version": "3.6.7",
4
4
  "description": "Emoji plugin for @atlaskit/editor-core",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -23,22 +23,22 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@atlaskit/adf-schema": "^47.6.0",
26
- "@atlaskit/editor-common": "^105.0.0",
27
- "@atlaskit/editor-plugin-analytics": "^2.2.0",
26
+ "@atlaskit/editor-common": "^105.2.0",
27
+ "@atlaskit/editor-plugin-analytics": "^2.3.0",
28
28
  "@atlaskit/editor-plugin-annotation": "^2.8.0",
29
29
  "@atlaskit/editor-plugin-base": "^3.0.0",
30
30
  "@atlaskit/editor-plugin-editor-viewmode": "^3.1.0",
31
31
  "@atlaskit/editor-plugin-metrics": "^3.4.0",
32
- "@atlaskit/editor-plugin-type-ahead": "^2.6.0",
32
+ "@atlaskit/editor-plugin-type-ahead": "^2.7.0",
33
33
  "@atlaskit/editor-prosemirror": "7.0.0",
34
34
  "@atlaskit/editor-shared-styles": "^3.4.0",
35
- "@atlaskit/emoji": "^69.1.0",
35
+ "@atlaskit/emoji": "^69.2.0",
36
36
  "@atlaskit/icon": "^26.0.0",
37
37
  "@atlaskit/node-data-provider": "^4.1.0",
38
38
  "@atlaskit/platform-feature-flags": "^1.1.0",
39
39
  "@atlaskit/prosemirror-input-rules": "^3.3.0",
40
40
  "@atlaskit/theme": "^18.0.0",
41
- "@atlaskit/tmp-editor-statsig": "^4.19.0",
41
+ "@atlaskit/tmp-editor-statsig": "^4.21.0",
42
42
  "@atlaskit/tokens": "^4.8.0",
43
43
  "@babel/runtime": "^7.0.0",
44
44
  "@emotion/react": "^11.7.1",