@atlaskit/reactions 35.13.8 → 36.0.0

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,17 @@
1
1
  # @atlaskit/reactions
2
2
 
3
+ ## 36.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`1b02e7b19fed0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1b02e7b19fed0) -
8
+ [ux] This is for adding a reaction failure callback and reverting optimistic add/remove updates
9
+ when the request fails. https://customerfeedback.atlassian.net/browse/MWIV-1073
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
3
15
  ## 35.13.8
4
16
 
5
17
  ### Patch Changes
@@ -11,7 +11,7 @@ var _analyticsGasTypes = require("@atlaskit/analytics-gas-types");
11
11
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
12
12
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // eslint-disable-next-line @typescript-eslint/consistent-type-imports
13
13
  var packageName = "@atlaskit/reactions";
14
- var packageVersion = "35.13.7";
14
+ var packageVersion = "35.13.8";
15
15
  /**
16
16
  * TODO: move to utility package?
17
17
  * A random sampling function
@@ -18,7 +18,7 @@ var _components = require("../../components");
18
18
  var _Reactions = require("../../components/Reactions");
19
19
  var _store2 = require("../../store");
20
20
  var _types = require("../../types");
21
- var _excluded = ["ari", "containerAri", "store", "particleEffectByEmojiEnabled", "onReactionSuccess"];
21
+ var _excluded = ["ari", "containerAri", "store", "particleEffectByEmojiEnabled", "onReactionSuccess", "onReactionFailure"];
22
22
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
23
23
  /**
24
24
  * State Props required for <Reactions />
@@ -63,26 +63,42 @@ var mapStateToPropsHelper = exports.mapStateToPropsHelper = function mapStateToP
63
63
  /**
64
64
  * Export the mapper function outside the component so easier to do unit tests
65
65
  */
66
- var mapDispatchToPropsHelper = exports.mapDispatchToPropsHelper = function mapDispatchToPropsHelper(actions, containerAri, ari, successCallBack) {
66
+ var mapDispatchToPropsHelper = exports.mapDispatchToPropsHelper = function mapDispatchToPropsHelper(actions, containerAri, ari, successCallBack, failureCallBack) {
67
67
  return {
68
68
  loadReaction: function loadReaction() {
69
69
  actions.getReactions(containerAri, ari);
70
70
  },
71
71
  onReactionClick: function onReactionClick(emojiId) {
72
+ if (successCallBack && failureCallBack) {
73
+ actions.toggleReaction(containerAri, ari, emojiId, successCallBack, failureCallBack);
74
+ return;
75
+ }
72
76
  if (successCallBack) {
73
77
  actions.toggleReaction(containerAri, ari, emojiId, successCallBack);
74
78
  return;
75
79
  }
80
+ if (failureCallBack) {
81
+ actions.toggleReaction(containerAri, ari, emojiId, undefined, failureCallBack);
82
+ return;
83
+ }
76
84
  actions.toggleReaction(containerAri, ari, emojiId);
77
85
  },
78
86
  getReactionDetails: function getReactionDetails(emojiId) {
79
87
  actions.getDetailedReaction(containerAri, ari, emojiId);
80
88
  },
81
89
  onSelection: function onSelection(emojiId) {
90
+ if (successCallBack && failureCallBack) {
91
+ actions.addReaction(containerAri, ari, emojiId, successCallBack, failureCallBack);
92
+ return;
93
+ }
82
94
  if (successCallBack) {
83
95
  actions.addReaction(containerAri, ari, emojiId, successCallBack);
84
96
  return;
85
97
  }
98
+ if (failureCallBack) {
99
+ actions.addReaction(containerAri, ari, emojiId, undefined, failureCallBack);
100
+ return;
101
+ }
86
102
  actions.addReaction(containerAri, ari, emojiId);
87
103
  }
88
104
  };
@@ -93,6 +109,7 @@ var ConnectedReactionsView = exports.ConnectedReactionsView = function Connected
93
109
  store = props.store,
94
110
  particleEffectByEmojiEnabled = props.particleEffectByEmojiEnabled,
95
111
  onReactionSuccess = props.onReactionSuccess,
112
+ onReactionFailure = props.onReactionFailure,
96
113
  rest = (0, _objectWithoutProperties2.default)(props, _excluded);
97
114
  /**
98
115
  * Reference to the <Reactions /> component instance mandatory props
@@ -141,8 +158,8 @@ var ConnectedReactionsView = exports.ConnectedReactionsView = function Connected
141
158
  * @param actions list of actions in the store instance
142
159
  */
143
160
  var mapDispatchToProps = (0, _react.useCallback)(function (actions) {
144
- return mapDispatchToPropsHelper(actions, containerAri, ari, onReactionSuccess);
145
- }, [ari, containerAri, onReactionSuccess]);
161
+ return mapDispatchToPropsHelper(actions, containerAri, ari, onReactionSuccess, onReactionFailure);
162
+ }, [ari, containerAri, onReactionSuccess, onReactionFailure]);
146
163
  var resolveStore = (0, _react.useCallback)(function (store) {
147
164
  if (store.setCreateAnalyticsEvent && createAnalyticsEvent) {
148
165
  store.setCreateAnalyticsEvent(createAnalyticsEvent);
@@ -15,6 +15,7 @@ var _utils = require("./utils");
15
15
  var _constants = require("../shared/constants");
16
16
  var _ufo = require("../ufo");
17
17
  var _reaction = require("../types/reaction");
18
+ var _expVal = require("@atlaskit/platform-feature-experiments/exp-val");
18
19
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
19
20
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
20
21
  /**
@@ -134,7 +135,7 @@ var MemoryReactionsStore = exports.MemoryReactionsStore = /*#__PURE__*/function
134
135
  });
135
136
  };
136
137
  });
137
- (0, _defineProperty2.default)(this, "doAddReaction", function (reaction, onSuccess) {
138
+ (0, _defineProperty2.default)(this, "doAddReaction", function (reaction, onSuccess, onFailure) {
138
139
  var containerAri = reaction.containerAri,
139
140
  ari = reaction.ari,
140
141
  emojiId = reaction.emojiId;
@@ -169,10 +170,15 @@ var MemoryReactionsStore = exports.MemoryReactionsStore = /*#__PURE__*/function
169
170
  }
170
171
  });
171
172
  }
173
+ if (onFailure && (0, _expVal.expVal)('exp-comment-reaction-fail-fix', 'isEnabled', false)) {
174
+ // revert the optimistic update since the request failed
175
+ _this.optmisticUpdate(containerAri, ari, emojiId)(_utils.removeOne);
176
+ onFailure(_reaction.ReactionUpdateType.added, ari, emojiId, error);
177
+ }
172
178
  return Promise.reject(error);
173
179
  });
174
180
  });
175
- (0, _defineProperty2.default)(this, "doRemoveReaction", function (reaction, onSuccess) {
181
+ (0, _defineProperty2.default)(this, "doRemoveReaction", function (reaction, onSuccess, onFailure) {
176
182
  var containerAri = reaction.containerAri,
177
183
  ari = reaction.ari,
178
184
  emojiId = reaction.emojiId;
@@ -202,6 +208,11 @@ var MemoryReactionsStore = exports.MemoryReactionsStore = /*#__PURE__*/function
202
208
  reason: 'deleteReaction fetch failed'
203
209
  }
204
210
  });
211
+ if (onFailure && (0, _expVal.expVal)('exp-comment-reaction-fail-fix', 'isEnabled', false)) {
212
+ // revert the optimistic update since the request failed
213
+ _this.optmisticUpdate(containerAri, ari, emojiId)(_utils.addOne);
214
+ onFailure(_reaction.ReactionUpdateType.removed, ari, emojiId, error);
215
+ }
205
216
  });
206
217
  });
207
218
  (0, _defineProperty2.default)(this, "setCreateAnalyticsEvent", function (createAnalyticsEvent) {
@@ -362,7 +373,7 @@ var MemoryReactionsStore = exports.MemoryReactionsStore = /*#__PURE__*/function
362
373
  key: "withReaction",
363
374
  value: function withReaction(reactedCallback, notReactedCallback) {
364
375
  var _this3 = this;
365
- return function (containerAri, ari, emojiId, onSuccess) {
376
+ return function (containerAri, ari, emojiId, onSuccess, onFailure) {
366
377
  _this3.withReadyReaction(containerAri, ari)(function (reactionsState) {
367
378
  var reaction = reactionsState.reactions.find((0, _utils.byEmojiId)(emojiId)) || {
368
379
  containerAri: containerAri,
@@ -372,7 +383,7 @@ var MemoryReactionsStore = exports.MemoryReactionsStore = /*#__PURE__*/function
372
383
  reacted: false
373
384
  };
374
385
  var callback = reaction.reacted || !notReactedCallback ? reactedCallback : notReactedCallback;
375
- var updatedReaction = callback(reaction, onSuccess);
386
+ var updatedReaction = callback(reaction, onSuccess, onFailure);
376
387
  if (updatedReaction && !(updatedReaction instanceof Function)) {
377
388
  return (0, _utils.readyState)(reactionsState.reactions.map((0, _utils.updateByEmojiId)(emojiId, updatedReaction)));
378
389
  }
@@ -40,6 +40,14 @@ var ReactionUpdateType = exports.ReactionUpdateType = /*#__PURE__*/function (Rea
40
40
  * @param ari - The ARI (Atlassian Resource Identifier) of the entity.
41
41
  * @param emojiId - The ID of the emoji used in the reaction.
42
42
  */
43
+ /**
44
+ * Event handler that is invoked when a reaction update (add/remove) fails.
45
+ *
46
+ * @param {ReactionUpdateType} action - The action that was attempted on the reaction.
47
+ * @param ari - The ARI (Atlassian Resource Identifier) of the entity.
48
+ * @param emojiId - The ID of the emoji used in the reaction.
49
+ * @param error - The error raised by the underlying reaction service call.
50
+ */
43
51
  /**
44
52
  * Condition of the reaction when gets loaded from the store
45
53
  */
@@ -2,7 +2,7 @@
2
2
  import createAndFireEvent from '@atlaskit/analytics-next/createAndFireEvents';
3
3
  import { UI_EVENT_TYPE, OPERATIONAL_EVENT_TYPE } from '@atlaskit/analytics-gas-types';
4
4
  const packageName = "@atlaskit/reactions";
5
- const packageVersion = "35.13.7";
5
+ const packageVersion = "35.13.8";
6
6
  /**
7
7
  * TODO: move to utility package?
8
8
  * A random sampling function
@@ -50,26 +50,42 @@ export const mapStateToPropsHelper = (containerAri, ari, particleEffectByEmojiEn
50
50
  /**
51
51
  * Export the mapper function outside the component so easier to do unit tests
52
52
  */
53
- export const mapDispatchToPropsHelper = (actions, containerAri, ari, successCallBack) => {
53
+ export const mapDispatchToPropsHelper = (actions, containerAri, ari, successCallBack, failureCallBack) => {
54
54
  return {
55
55
  loadReaction: () => {
56
56
  actions.getReactions(containerAri, ari);
57
57
  },
58
58
  onReactionClick: emojiId => {
59
+ if (successCallBack && failureCallBack) {
60
+ actions.toggleReaction(containerAri, ari, emojiId, successCallBack, failureCallBack);
61
+ return;
62
+ }
59
63
  if (successCallBack) {
60
64
  actions.toggleReaction(containerAri, ari, emojiId, successCallBack);
61
65
  return;
62
66
  }
67
+ if (failureCallBack) {
68
+ actions.toggleReaction(containerAri, ari, emojiId, undefined, failureCallBack);
69
+ return;
70
+ }
63
71
  actions.toggleReaction(containerAri, ari, emojiId);
64
72
  },
65
73
  getReactionDetails: emojiId => {
66
74
  actions.getDetailedReaction(containerAri, ari, emojiId);
67
75
  },
68
76
  onSelection: emojiId => {
77
+ if (successCallBack && failureCallBack) {
78
+ actions.addReaction(containerAri, ari, emojiId, successCallBack, failureCallBack);
79
+ return;
80
+ }
69
81
  if (successCallBack) {
70
82
  actions.addReaction(containerAri, ari, emojiId, successCallBack);
71
83
  return;
72
84
  }
85
+ if (failureCallBack) {
86
+ actions.addReaction(containerAri, ari, emojiId, undefined, failureCallBack);
87
+ return;
88
+ }
73
89
  actions.addReaction(containerAri, ari, emojiId);
74
90
  }
75
91
  };
@@ -81,6 +97,7 @@ export const ConnectedReactionsView = props => {
81
97
  store,
82
98
  particleEffectByEmojiEnabled,
83
99
  onReactionSuccess,
100
+ onReactionFailure,
84
101
  ...rest
85
102
  } = props;
86
103
  /**
@@ -125,8 +142,8 @@ export const ConnectedReactionsView = props => {
125
142
  * @param actions list of actions in the store instance
126
143
  */
127
144
  const mapDispatchToProps = useCallback(actions => {
128
- return mapDispatchToPropsHelper(actions, containerAri, ari, onReactionSuccess);
129
- }, [ari, containerAri, onReactionSuccess]);
145
+ return mapDispatchToPropsHelper(actions, containerAri, ari, onReactionSuccess, onReactionFailure);
146
+ }, [ari, containerAri, onReactionSuccess, onReactionFailure]);
130
147
  const resolveStore = useCallback(store => {
131
148
  if (store.setCreateAnalyticsEvent && createAnalyticsEvent) {
132
149
  store.setCreateAnalyticsEvent(createAnalyticsEvent);
@@ -6,6 +6,8 @@ import { addOne, byEmojiId, flattenAris, getReactionsSortFunction, isRealErrorFr
6
6
  import { SAMPLING_RATE_REACTIONS_RENDERED_EXP } from '../shared/constants';
7
7
  import { ReactionsAdd, ReactionDetailsFetch, ReactionsRemove, sampledReactionsRendered } from '../ufo';
8
8
  import { ReactionUpdateType } from '../types/reaction';
9
+ import { expVal } from '@atlaskit/platform-feature-experiments/exp-val';
10
+
9
11
  /**
10
12
  * Set of all available UFO experiences relating to reaction element
11
13
  */
@@ -121,7 +123,7 @@ export class MemoryReactionsStore {
121
123
  return readyState(reactions);
122
124
  });
123
125
  });
124
- _defineProperty(this, "doAddReaction", (reaction, onSuccess) => {
126
+ _defineProperty(this, "doAddReaction", (reaction, onSuccess, onFailure) => {
125
127
  const {
126
128
  containerAri,
127
129
  ari,
@@ -158,10 +160,15 @@ export class MemoryReactionsStore {
158
160
  }
159
161
  });
160
162
  }
163
+ if (onFailure && expVal('exp-comment-reaction-fail-fix', 'isEnabled', false)) {
164
+ // revert the optimistic update since the request failed
165
+ this.optmisticUpdate(containerAri, ari, emojiId)(removeOne);
166
+ onFailure(ReactionUpdateType.added, ari, emojiId, error);
167
+ }
161
168
  return Promise.reject(error);
162
169
  });
163
170
  });
164
- _defineProperty(this, "doRemoveReaction", (reaction, onSuccess) => {
171
+ _defineProperty(this, "doRemoveReaction", (reaction, onSuccess, onFailure) => {
165
172
  const {
166
173
  containerAri,
167
174
  ari,
@@ -193,6 +200,11 @@ export class MemoryReactionsStore {
193
200
  reason: 'deleteReaction fetch failed'
194
201
  }
195
202
  });
203
+ if (onFailure && expVal('exp-comment-reaction-fail-fix', 'isEnabled', false)) {
204
+ // revert the optimistic update since the request failed
205
+ this.optmisticUpdate(containerAri, ari, emojiId)(addOne);
206
+ onFailure(ReactionUpdateType.removed, ari, emojiId, error);
207
+ }
196
208
  });
197
209
  });
198
210
  _defineProperty(this, "setCreateAnalyticsEvent", createAnalyticsEvent => {
@@ -353,7 +365,7 @@ export class MemoryReactionsStore {
353
365
  * emojiId. If some state is returned, the new state will be applied.
354
366
  */
355
367
  withReaction(reactedCallback, notReactedCallback) {
356
- return (containerAri, ari, emojiId, onSuccess) => {
368
+ return (containerAri, ari, emojiId, onSuccess, onFailure) => {
357
369
  this.withReadyReaction(containerAri, ari)(reactionsState => {
358
370
  const reaction = reactionsState.reactions.find(byEmojiId(emojiId)) || {
359
371
  containerAri,
@@ -363,7 +375,7 @@ export class MemoryReactionsStore {
363
375
  reacted: false
364
376
  };
365
377
  const callback = reaction.reacted || !notReactedCallback ? reactedCallback : notReactedCallback;
366
- const updatedReaction = callback(reaction, onSuccess);
378
+ const updatedReaction = callback(reaction, onSuccess, onFailure);
367
379
  if (updatedReaction && !(updatedReaction instanceof Function)) {
368
380
  return readyState(reactionsState.reactions.map(updateByEmojiId(emojiId, updatedReaction)));
369
381
  }
@@ -41,6 +41,15 @@ export let ReactionUpdateType = /*#__PURE__*/function (ReactionUpdateType) {
41
41
  * @param emojiId - The ID of the emoji used in the reaction.
42
42
  */
43
43
 
44
+ /**
45
+ * Event handler that is invoked when a reaction update (add/remove) fails.
46
+ *
47
+ * @param {ReactionUpdateType} action - The action that was attempted on the reaction.
48
+ * @param ari - The ARI (Atlassian Resource Identifier) of the entity.
49
+ * @param emojiId - The ID of the emoji used in the reaction.
50
+ * @param error - The error raised by the underlying reaction service call.
51
+ */
52
+
44
53
  /**
45
54
  * Condition of the reaction when gets loaded from the store
46
55
  */
@@ -5,7 +5,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
5
5
  import createAndFireEvent from '@atlaskit/analytics-next/createAndFireEvents';
6
6
  import { UI_EVENT_TYPE, OPERATIONAL_EVENT_TYPE } from '@atlaskit/analytics-gas-types';
7
7
  var packageName = "@atlaskit/reactions";
8
- var packageVersion = "35.13.7";
8
+ var packageVersion = "35.13.8";
9
9
  /**
10
10
  * TODO: move to utility package?
11
11
  * A random sampling function
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/extends";
2
2
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
3
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
4
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
5
- var _excluded = ["ari", "containerAri", "store", "particleEffectByEmojiEnabled", "onReactionSuccess"];
5
+ var _excluded = ["ari", "containerAri", "store", "particleEffectByEmojiEnabled", "onReactionSuccess", "onReactionFailure"];
6
6
  import _regeneratorRuntime from "@babel/runtime/regenerator";
7
7
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
8
8
  import { FabricElementsAnalyticsContext } from '@atlaskit/analytics-namespaced-context/FabricElementsAnalyticsContext';
@@ -55,26 +55,42 @@ export var mapStateToPropsHelper = function mapStateToPropsHelper(containerAri,
55
55
  /**
56
56
  * Export the mapper function outside the component so easier to do unit tests
57
57
  */
58
- export var mapDispatchToPropsHelper = function mapDispatchToPropsHelper(actions, containerAri, ari, successCallBack) {
58
+ export var mapDispatchToPropsHelper = function mapDispatchToPropsHelper(actions, containerAri, ari, successCallBack, failureCallBack) {
59
59
  return {
60
60
  loadReaction: function loadReaction() {
61
61
  actions.getReactions(containerAri, ari);
62
62
  },
63
63
  onReactionClick: function onReactionClick(emojiId) {
64
+ if (successCallBack && failureCallBack) {
65
+ actions.toggleReaction(containerAri, ari, emojiId, successCallBack, failureCallBack);
66
+ return;
67
+ }
64
68
  if (successCallBack) {
65
69
  actions.toggleReaction(containerAri, ari, emojiId, successCallBack);
66
70
  return;
67
71
  }
72
+ if (failureCallBack) {
73
+ actions.toggleReaction(containerAri, ari, emojiId, undefined, failureCallBack);
74
+ return;
75
+ }
68
76
  actions.toggleReaction(containerAri, ari, emojiId);
69
77
  },
70
78
  getReactionDetails: function getReactionDetails(emojiId) {
71
79
  actions.getDetailedReaction(containerAri, ari, emojiId);
72
80
  },
73
81
  onSelection: function onSelection(emojiId) {
82
+ if (successCallBack && failureCallBack) {
83
+ actions.addReaction(containerAri, ari, emojiId, successCallBack, failureCallBack);
84
+ return;
85
+ }
74
86
  if (successCallBack) {
75
87
  actions.addReaction(containerAri, ari, emojiId, successCallBack);
76
88
  return;
77
89
  }
90
+ if (failureCallBack) {
91
+ actions.addReaction(containerAri, ari, emojiId, undefined, failureCallBack);
92
+ return;
93
+ }
78
94
  actions.addReaction(containerAri, ari, emojiId);
79
95
  }
80
96
  };
@@ -85,6 +101,7 @@ export var ConnectedReactionsView = function ConnectedReactionsView(props) {
85
101
  store = props.store,
86
102
  particleEffectByEmojiEnabled = props.particleEffectByEmojiEnabled,
87
103
  onReactionSuccess = props.onReactionSuccess,
104
+ onReactionFailure = props.onReactionFailure,
88
105
  rest = _objectWithoutProperties(props, _excluded);
89
106
  /**
90
107
  * Reference to the <Reactions /> component instance mandatory props
@@ -133,8 +150,8 @@ export var ConnectedReactionsView = function ConnectedReactionsView(props) {
133
150
  * @param actions list of actions in the store instance
134
151
  */
135
152
  var mapDispatchToProps = useCallback(function (actions) {
136
- return mapDispatchToPropsHelper(actions, containerAri, ari, onReactionSuccess);
137
- }, [ari, containerAri, onReactionSuccess]);
153
+ return mapDispatchToPropsHelper(actions, containerAri, ari, onReactionSuccess, onReactionFailure);
154
+ }, [ari, containerAri, onReactionSuccess, onReactionFailure]);
138
155
  var resolveStore = useCallback(function (store) {
139
156
  if (store.setCreateAnalyticsEvent && createAnalyticsEvent) {
140
157
  store.setCreateAnalyticsEvent(createAnalyticsEvent);
@@ -10,6 +10,8 @@ import { addOne, byEmojiId, flattenAris, getReactionsSortFunction, isRealErrorFr
10
10
  import { SAMPLING_RATE_REACTIONS_RENDERED_EXP } from '../shared/constants';
11
11
  import { ReactionsAdd, ReactionDetailsFetch, ReactionsRemove, sampledReactionsRendered } from '../ufo';
12
12
  import { ReactionUpdateType } from '../types/reaction';
13
+ import { expVal } from '@atlaskit/platform-feature-experiments/exp-val';
14
+
13
15
  /**
14
16
  * Set of all available UFO experiences relating to reaction element
15
17
  */
@@ -128,7 +130,7 @@ export var MemoryReactionsStore = /*#__PURE__*/function () {
128
130
  });
129
131
  };
130
132
  });
131
- _defineProperty(this, "doAddReaction", function (reaction, onSuccess) {
133
+ _defineProperty(this, "doAddReaction", function (reaction, onSuccess, onFailure) {
132
134
  var containerAri = reaction.containerAri,
133
135
  ari = reaction.ari,
134
136
  emojiId = reaction.emojiId;
@@ -163,10 +165,15 @@ export var MemoryReactionsStore = /*#__PURE__*/function () {
163
165
  }
164
166
  });
165
167
  }
168
+ if (onFailure && expVal('exp-comment-reaction-fail-fix', 'isEnabled', false)) {
169
+ // revert the optimistic update since the request failed
170
+ _this.optmisticUpdate(containerAri, ari, emojiId)(removeOne);
171
+ onFailure(ReactionUpdateType.added, ari, emojiId, error);
172
+ }
166
173
  return Promise.reject(error);
167
174
  });
168
175
  });
169
- _defineProperty(this, "doRemoveReaction", function (reaction, onSuccess) {
176
+ _defineProperty(this, "doRemoveReaction", function (reaction, onSuccess, onFailure) {
170
177
  var containerAri = reaction.containerAri,
171
178
  ari = reaction.ari,
172
179
  emojiId = reaction.emojiId;
@@ -196,6 +203,11 @@ export var MemoryReactionsStore = /*#__PURE__*/function () {
196
203
  reason: 'deleteReaction fetch failed'
197
204
  }
198
205
  });
206
+ if (onFailure && expVal('exp-comment-reaction-fail-fix', 'isEnabled', false)) {
207
+ // revert the optimistic update since the request failed
208
+ _this.optmisticUpdate(containerAri, ari, emojiId)(addOne);
209
+ onFailure(ReactionUpdateType.removed, ari, emojiId, error);
210
+ }
199
211
  });
200
212
  });
201
213
  _defineProperty(this, "setCreateAnalyticsEvent", function (createAnalyticsEvent) {
@@ -356,7 +368,7 @@ export var MemoryReactionsStore = /*#__PURE__*/function () {
356
368
  key: "withReaction",
357
369
  value: function withReaction(reactedCallback, notReactedCallback) {
358
370
  var _this3 = this;
359
- return function (containerAri, ari, emojiId, onSuccess) {
371
+ return function (containerAri, ari, emojiId, onSuccess, onFailure) {
360
372
  _this3.withReadyReaction(containerAri, ari)(function (reactionsState) {
361
373
  var reaction = reactionsState.reactions.find(byEmojiId(emojiId)) || {
362
374
  containerAri: containerAri,
@@ -366,7 +378,7 @@ export var MemoryReactionsStore = /*#__PURE__*/function () {
366
378
  reacted: false
367
379
  };
368
380
  var callback = reaction.reacted || !notReactedCallback ? reactedCallback : notReactedCallback;
369
- var updatedReaction = callback(reaction, onSuccess);
381
+ var updatedReaction = callback(reaction, onSuccess, onFailure);
370
382
  if (updatedReaction && !(updatedReaction instanceof Function)) {
371
383
  return readyState(reactionsState.reactions.map(updateByEmojiId(emojiId, updatedReaction)));
372
384
  }
@@ -41,6 +41,15 @@ export var ReactionUpdateType = /*#__PURE__*/function (ReactionUpdateType) {
41
41
  * @param emojiId - The ID of the emoji used in the reaction.
42
42
  */
43
43
 
44
+ /**
45
+ * Event handler that is invoked when a reaction update (add/remove) fails.
46
+ *
47
+ * @param {ReactionUpdateType} action - The action that was attempted on the reaction.
48
+ * @param ari - The ARI (Atlassian Resource Identifier) of the entity.
49
+ * @param emojiId - The ID of the emoji used in the reaction.
50
+ * @param error - The error raised by the underlying reaction service call.
51
+ */
52
+
44
53
  /**
45
54
  * Condition of the reaction when gets loaded from the store
46
55
  */
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { type ReactionsProps, type ReactionPickerProps } from '../../components';
3
3
  import { ReactionStatus, type Actions, type State, type StorePropInput } from '../../types';
4
- import { type ReactionSummary, type ReactionUpdateSuccess } from '../../types/reaction';
4
+ import { type ReactionSummary, type ReactionUpdateSuccess, type ReactionUpdateFailure } from '../../types/reaction';
5
5
  export interface ConnectedReactionsViewProps extends Pick<ReactionsProps, 'quickReactionEmojis' | 'onDialogOpenCallback' | 'onDialogCloseCallback' | 'onDialogSelectReactionCallback' | 'allowUserDialog' | 'allowAllEmojis' | 'emojiProvider' | 'emojiPickerSize' | 'miniMode' | 'summaryViewEnabled' | 'subtleReactionsSummaryAndPicker' | 'onlyRenderPicker' | 'isViewOnly' | 'allowSelectFromSummaryView' | 'reactionPickerPopperZIndex'>, Pick<ReactionPickerProps, 'pickerQuickReactionEmojiIds'> {
6
6
  /**
7
7
  * Individual id for a reaction
@@ -11,6 +11,10 @@ export interface ConnectedReactionsViewProps extends Pick<ReactionsProps, 'quick
11
11
  * Wrapper id for reactions list
12
12
  */
13
13
  containerAri: string;
14
+ /**
15
+ * Callback function when adding/removing a reaction fails
16
+ */
17
+ onReactionFailure?: ReactionUpdateFailure;
14
18
  /**
15
19
  * Callback function when a reaction is successfully added
16
20
  */
@@ -30,24 +34,24 @@ export interface ConnectedReactionsViewProps extends Pick<ReactionsProps, 'quick
30
34
  * Export the mapper function outside the component so easier to do unit tests
31
35
  */
32
36
  export declare const mapStateToPropsHelper: (containerAri: string, ari: string, particleEffectByEmojiEnabled?: boolean, state?: State) => {
33
- status: ReactionStatus;
34
- reactions: never[];
35
37
  flash?: undefined;
36
38
  particleEffectByEmoji?: undefined;
39
+ reactions: never[];
40
+ status: ReactionStatus;
37
41
  } | {
38
- reactions: ReactionSummary[];
39
- status: ReactionStatus.ready;
40
42
  flash: {
41
43
  [emojiId: string]: boolean;
42
44
  };
43
45
  particleEffectByEmoji: {
44
46
  [emojiId: string]: boolean;
45
47
  };
48
+ reactions: ReactionSummary[];
49
+ status: ReactionStatus.ready;
46
50
  };
47
51
  /**
48
52
  * Export the mapper function outside the component so easier to do unit tests
49
53
  */
50
- export declare const mapDispatchToPropsHelper: (actions: Actions, containerAri: string, ari: string, successCallBack?: ReactionUpdateSuccess) => {
54
+ export declare const mapDispatchToPropsHelper: (actions: Actions, containerAri: string, ari: string, successCallBack?: ReactionUpdateSuccess, failureCallBack?: ReactionUpdateFailure) => {
51
55
  loadReaction: () => void;
52
56
  onReactionClick: (emojiId: string) => void;
53
57
  getReactionDetails: (emojiId: string) => void;
@@ -1,6 +1,6 @@
1
1
  import type { CreateUIAnalyticsEvent } from '@atlaskit/analytics-next/types';
2
2
  import { type Client, type OnChangeCallback, type State, type Store } from '../types';
3
- import { type ReactionUpdateSuccess } from '../types/reaction';
3
+ import { type ReactionUpdateSuccess, type ReactionUpdateFailure } from '../types/reaction';
4
4
  import type { WithSamplingUFOExperience } from '@atlaskit/emoji';
5
5
  import type { ConcurrentExperience } from '@atlaskit/ufo/concurrent-experience';
6
6
  /**
@@ -11,6 +11,10 @@ export declare const ufoExperiences: {
11
11
  * Experience when a reaction emoji gets added
12
12
  */
13
13
  add: ConcurrentExperience;
14
+ /**
15
+ * Experience when a reaction details gets fetched
16
+ */
17
+ fetchDetails: ConcurrentExperience;
14
18
  /**
15
19
  * Experience when a reaction emoji gets removed/decrement
16
20
  */
@@ -19,10 +23,6 @@ export declare const ufoExperiences: {
19
23
  * Experience when the list of reactions gets rendered with sampling
20
24
  */
21
25
  render: (instanceId: string) => WithSamplingUFOExperience;
22
- /**
23
- * Experience when a reaction details gets fetched
24
- */
25
- fetchDetails: ConcurrentExperience;
26
26
  };
27
27
  /**
28
28
  * Optional metadata information in the store used in sending the API client requests
@@ -89,8 +89,8 @@ export declare class MemoryReactionsStore implements Store {
89
89
  private doRemoveReaction;
90
90
  setCreateAnalyticsEvent: (createAnalyticsEvent?: CreateUIAnalyticsEvent) => void;
91
91
  getReactions: (key: string, ...args: string[]) => void;
92
- toggleReaction: (containerAri: string, ari: string, emojiId: string, onSuccess?: ReactionUpdateSuccess) => void;
93
- addReaction: (containerAri: string, ari: string, emojiId: string, onSuccess?: ReactionUpdateSuccess) => void;
92
+ toggleReaction: (containerAri: string, ari: string, emojiId: string, onSuccess?: ReactionUpdateSuccess, onFailure?: ReactionUpdateFailure) => void;
93
+ addReaction: (containerAri: string, ari: string, emojiId: string, onSuccess?: ReactionUpdateSuccess, onFailure?: ReactionUpdateFailure) => void;
94
94
  getDetailedReaction: (containerAri: string, ari: string, emojiId: string) => void;
95
95
  getState: () => State;
96
96
  onChange: (callback: OnChangeCallback) => void;
@@ -1,11 +1,13 @@
1
- import { type ReactionUpdateSuccess } from './reaction';
1
+ import { type ReactionUpdateFailure, type ReactionUpdateSuccess } from './reaction';
2
2
  /**
3
3
  * Event callback by the client to the API
4
4
  * @param containerAri the container for reactions/ari in the page (attached to the "quickReactionEmojis" prop)
5
5
  * @param ariunique Atlassian identifier for an emoji (attached to the "quickReactionEmojis" prop)
6
6
  * @param emojiId unique identifier guid for the emoji
7
+ * @param onSuccess (Optional) callback invoked when the reaction update succeeds
8
+ * @param onFailure (Optional) callback invoked when the reaction update fails
7
9
  */
8
- export type ReactionAction = (containerAri: string, ari: string, emojiId: string, onSuccess?: ReactionUpdateSuccess) => void;
10
+ export type ReactionAction = (containerAri: string, ari: string, emojiId: string, onSuccess?: ReactionUpdateSuccess, onFailure?: ReactionUpdateFailure) => void;
9
11
  export type Actions = {
10
12
  /**
11
13
  * Add a new reation api
@@ -1,2 +1,2 @@
1
- import { type ReactionUpdateSuccess } from './reaction';
2
- export type Updater<T> = (original: T, onSuccess?: ReactionUpdateSuccess) => T | void;
1
+ import { type ReactionUpdateFailure, type ReactionUpdateSuccess } from './reaction';
2
+ export type Updater<T> = (original: T, onSuccess?: ReactionUpdateSuccess, onFailure?: ReactionUpdateFailure) => T | void;
@@ -83,6 +83,15 @@ export declare enum ReactionUpdateType {
83
83
  * @param emojiId - The ID of the emoji used in the reaction.
84
84
  */
85
85
  export type ReactionUpdateSuccess = (action: ReactionUpdateType, ari: string, emojiId: string, count: number) => void;
86
+ /**
87
+ * Event handler that is invoked when a reaction update (add/remove) fails.
88
+ *
89
+ * @param {ReactionUpdateType} action - The action that was attempted on the reaction.
90
+ * @param ari - The ARI (Atlassian Resource Identifier) of the entity.
91
+ * @param emojiId - The ID of the emoji used in the reaction.
92
+ * @param error - The error raised by the underlying reaction service call.
93
+ */
94
+ export type ReactionUpdateFailure = (action: ReactionUpdateType, ari: string, emojiId: string, error: unknown) => void;
86
95
  /**
87
96
  * Condition of the reaction when gets loaded from the store
88
97
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/reactions",
3
- "version": "35.13.8",
3
+ "version": "36.0.0",
4
4
  "description": "Reactions component",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -55,7 +55,7 @@
55
55
  "@atlaskit/spinner": "^20.3.0",
56
56
  "@atlaskit/tabs": "^21.1.0",
57
57
  "@atlaskit/theme": "^28.1.0",
58
- "@atlaskit/tmp-editor-statsig": "^161.0.0",
58
+ "@atlaskit/tmp-editor-statsig": "^162.0.0",
59
59
  "@atlaskit/tokens": "^16.8.0",
60
60
  "@atlaskit/tooltip": "^24.2.0",
61
61
  "@atlaskit/ufo": "^1.0.0",