@atlaskit/emoji 64.5.0 → 64.6.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/admin/package.json +1 -0
  3. package/dist/cjs/api/EmojiResource.js +57 -5
  4. package/dist/cjs/api/media/SiteEmojiResource.js +59 -2
  5. package/dist/cjs/api/media/TokenManager.js +16 -7
  6. package/dist/cjs/components/common/CachingEmoji.js +32 -68
  7. package/dist/cjs/version.json +1 -1
  8. package/dist/es2019/api/EmojiResource.js +21 -7
  9. package/dist/es2019/api/media/SiteEmojiResource.js +24 -0
  10. package/dist/es2019/api/media/TokenManager.js +13 -5
  11. package/dist/es2019/components/common/CachingEmoji.js +27 -70
  12. package/dist/es2019/version.json +1 -1
  13. package/dist/esm/api/EmojiResource.js +56 -6
  14. package/dist/esm/api/media/SiteEmojiResource.js +57 -2
  15. package/dist/esm/api/media/TokenManager.js +14 -5
  16. package/dist/esm/components/common/CachingEmoji.js +33 -69
  17. package/dist/esm/version.json +1 -1
  18. package/dist/types/api/EmojiResource.d.ts +5 -0
  19. package/dist/types/api/EmojiUtils.d.ts +1 -1
  20. package/dist/types/api/media/SiteEmojiResource.d.ts +4 -0
  21. package/dist/types/api/media/TokenManager.d.ts +2 -1
  22. package/dist/types/components/common/CachingEmoji.d.ts +1 -3
  23. package/dist/types/components/common/EmojiUploadPicker.d.ts +2 -2
  24. package/dist/types/components/common/ToneSelector.d.ts +1 -1
  25. package/dist/types/components/picker/EmojiPicker.d.ts +1 -1
  26. package/dist/types/components/picker/EmojiPickerComponent.d.ts +2 -2
  27. package/dist/types/components/typeahead/EmojiTypeAheadComponent.d.ts +2 -2
  28. package/dist/types/components/uploader/EmojiUploader.d.ts +1 -1
  29. package/dist/types/context/LegacyEmojiContextProvider.d.ts +3 -3
  30. package/dist/types/types.d.ts +6 -0
  31. package/dist/types/util/analytics/analytics.d.ts +21 -21
  32. package/element/package.json +1 -0
  33. package/package.json +5 -6
  34. package/picker/package.json +1 -0
  35. package/resource/package.json +1 -0
  36. package/typeahead/package.json +1 -0
  37. package/types/package.json +1 -0
  38. package/utils/package.json +1 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @atlaskit/emoji
2
2
 
3
+ ## 64.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`b926172a999`](https://bitbucket.org/atlassian/atlassian-frontend/commits/b926172a999) - Custom Emoji Assets now load using inline media tokens preventing 401s
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+
13
+ ## 64.5.2
14
+
15
+ ### Patch Changes
16
+
17
+ - [`a424e62b264`](https://bitbucket.org/atlassian/atlassian-frontend/commits/a424e62b264) - Changes to support Node 16 Typescript definitions from `@types/node`.
18
+
19
+ ## 64.5.1
20
+
21
+ ### Patch Changes
22
+
23
+ - [`cb2392f6d33`](https://bitbucket.org/atlassian/atlassian-frontend/commits/cb2392f6d33) - Upgrade to TypeScript 4.2.4
24
+ - Updated dependencies
25
+
3
26
  ## 64.5.0
4
27
 
5
28
  ### Minor Changes
@@ -3,5 +3,6 @@
3
3
  "main": "../dist/cjs/admin.js",
4
4
  "module": "../dist/esm/admin.js",
5
5
  "module:es2019": "../dist/es2019/admin.js",
6
+ "sideEffects": false,
6
7
  "types": "../dist/types/admin.d.ts"
7
8
  }
@@ -7,6 +7,10 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports.supportsUploadFeature = exports.default = exports.EmojiResource = void 0;
9
9
 
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
10
14
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
15
 
12
16
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
@@ -43,6 +47,10 @@ var _SiteEmojiResource = _interopRequireDefault(require("./media/SiteEmojiResour
43
47
 
44
48
  var _analytics = require("../util/analytics");
45
49
 
50
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
51
+
52
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
53
+
46
54
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
47
55
 
48
56
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
@@ -52,13 +60,11 @@ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Re
52
60
  *
53
61
  * Follow this up with an isUploadSupported() check to see if the provider is actually
54
62
  * configured to support uploads.
63
+ * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
55
64
  */
56
65
  var supportsUploadFeature = function supportsUploadFeature(emojiProvider) {
57
- var _ref = emojiProvider,
58
- isUploadSupported = _ref.isUploadSupported,
59
- prepareForUpload = _ref.prepareForUpload,
60
- uploadCustomEmoji = _ref.uploadCustomEmoji;
61
- return !!(isUploadSupported && prepareForUpload && uploadCustomEmoji);
66
+ var emojiUploadProvider = emojiProvider;
67
+ return !!emojiUploadProvider.isUploadSupported && !!emojiUploadProvider.uploadCustomEmoji && !!emojiUploadProvider.prepareForUpload;
62
68
  };
63
69
 
64
70
  exports.supportsUploadFeature = supportsUploadFeature;
@@ -248,6 +254,52 @@ var EmojiResource = /*#__PURE__*/function (_AbstractResource) {
248
254
  (0, _get2.default)((0, _getPrototypeOf2.default)(EmojiResource.prototype), "notifyResult", this).call(this, result);
249
255
  }
250
256
  }
257
+ /**
258
+ * Returns the EmojiDescription with a valid media path that includes query token and client attributes to access the emoji media inline.
259
+ */
260
+
261
+ }, {
262
+ key: "getMediaEmojiDescriptionURLWithInlineToken",
263
+ value: function () {
264
+ var _getMediaEmojiDescriptionURLWithInlineToken = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(emoji) {
265
+ var tokenisedMediaPath;
266
+ return _regenerator.default.wrap(function _callee$(_context) {
267
+ while (1) {
268
+ switch (_context.prev = _context.next) {
269
+ case 0:
270
+ if (!(this.siteEmojiResource && (0, _typeHelpers.isMediaRepresentation)(emoji.representation))) {
271
+ _context.next = 5;
272
+ break;
273
+ }
274
+
275
+ _context.next = 3;
276
+ return this.siteEmojiResource.generateTokenisedMediaURL(emoji);
277
+
278
+ case 3:
279
+ tokenisedMediaPath = _context.sent;
280
+ return _context.abrupt("return", _objectSpread(_objectSpread({}, emoji), {}, {
281
+ representation: _objectSpread(_objectSpread({}, emoji.representation), {}, {
282
+ mediaPath: tokenisedMediaPath
283
+ })
284
+ }));
285
+
286
+ case 5:
287
+ return _context.abrupt("return", emoji);
288
+
289
+ case 6:
290
+ case "end":
291
+ return _context.stop();
292
+ }
293
+ }
294
+ }, _callee, this);
295
+ }));
296
+
297
+ function getMediaEmojiDescriptionURLWithInlineToken(_x) {
298
+ return _getMediaEmojiDescriptionURLWithInlineToken.apply(this, arguments);
299
+ }
300
+
301
+ return getMediaEmojiDescriptionURLWithInlineToken;
302
+ }()
251
303
  }, {
252
304
  key: "loadMediaEmoji",
253
305
  value: function loadMediaEmoji(emoji, useAlt) {
@@ -7,6 +7,10 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports.mediaProportionOfProgress = exports.default = void 0;
9
9
 
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
10
14
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
11
15
 
12
16
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
@@ -92,12 +96,65 @@ var SiteEmojiResource = /*#__PURE__*/function () {
92
96
  this.mediaEmojiCache = new _MediaEmojiCache.default(this.tokenManager);
93
97
  }
94
98
  /**
95
- * Will load media emoji, returning a new EmojiDescription if, for example,
96
- * the URL has changed.
99
+ * Will generate an emoji media path that is inclusive of client and token within the query parameter
97
100
  */
98
101
 
99
102
 
100
103
  (0, _createClass2.default)(SiteEmojiResource, [{
104
+ key: "generateTokenisedMediaURL",
105
+ value: function () {
106
+ var _generateTokenisedMediaURL = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(emoji) {
107
+ var currentMediaPathURL, currentMediaPathPARAMS, readToken;
108
+ return _regenerator.default.wrap(function _callee$(_context) {
109
+ while (1) {
110
+ switch (_context.prev = _context.next) {
111
+ case 0:
112
+ if (!(emoji && (0, _typeHelpers.isMediaRepresentation)(emoji.representation))) {
113
+ _context.next = 9;
114
+ break;
115
+ }
116
+
117
+ currentMediaPathURL = new URL(emoji.representation.mediaPath);
118
+ currentMediaPathPARAMS = currentMediaPathURL.searchParams;
119
+ _context.next = 5;
120
+ return this.tokenManager.getToken('read');
121
+
122
+ case 5:
123
+ readToken = _context.sent;
124
+
125
+ if (currentMediaPathPARAMS.get('token') !== readToken.jwt) {
126
+ currentMediaPathPARAMS.set('token', readToken.jwt);
127
+ }
128
+
129
+ if (currentMediaPathPARAMS.get('client') !== readToken.clientId) {
130
+ currentMediaPathPARAMS.set('client', readToken.clientId);
131
+ }
132
+
133
+ return _context.abrupt("return", currentMediaPathURL.toString());
134
+
135
+ case 9:
136
+ throw Error('Emoji resource is not of type Media Representation');
137
+
138
+ case 10:
139
+ case "end":
140
+ return _context.stop();
141
+ }
142
+ }
143
+ }, _callee, this);
144
+ }));
145
+
146
+ function generateTokenisedMediaURL(_x) {
147
+ return _generateTokenisedMediaURL.apply(this, arguments);
148
+ }
149
+
150
+ return generateTokenisedMediaURL;
151
+ }()
152
+ /**
153
+ * Will load media emoji, returning a new EmojiDescription if, for example,
154
+ * the URL has changed.
155
+ */
156
+
157
+ }, {
101
158
  key: "loadMediaEmoji",
102
159
  value: function loadMediaEmoji(emoji, useAlt) {
103
160
  if (!(0, _typeHelpers.isMediaEmoji)(emoji)) {
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.expireAdjustment = exports.default = void 0;
8
+ exports.default = exports.EXPIRES_AT_LATENCY_IN_SECONDS = void 0;
9
9
 
10
10
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
11
 
@@ -14,8 +14,8 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/creat
14
14
  var _utilServiceSupport = require("@atlaskit/util-service-support");
15
15
 
16
16
  // expire 30 seconds early to factor in latency, slow services, etc
17
- var expireAdjustment = 30;
18
- exports.expireAdjustment = expireAdjustment;
17
+ var EXPIRES_AT_LATENCY_IN_SECONDS = 30;
18
+ exports.EXPIRES_AT_LATENCY_IN_SECONDS = EXPIRES_AT_LATENCY_IN_SECONDS;
19
19
 
20
20
  var TokenManager = /*#__PURE__*/function () {
21
21
  function TokenManager(siteServiceConfig) {
@@ -25,6 +25,18 @@ var TokenManager = /*#__PURE__*/function () {
25
25
  }
26
26
 
27
27
  (0, _createClass2.default)(TokenManager, [{
28
+ key: "isValidToken",
29
+ value: function isValidToken(mediaApiToken) {
30
+ var nowInSeconds = Date.now() / 1000;
31
+ var expiresAt = mediaApiToken.expiresAt - EXPIRES_AT_LATENCY_IN_SECONDS;
32
+
33
+ if (nowInSeconds < expiresAt) {
34
+ return true;
35
+ }
36
+
37
+ return false;
38
+ }
39
+ }, {
28
40
  key: "addToken",
29
41
  value: function addToken(type, mediaApiToken) {
30
42
  this.tokens.set(type, {
@@ -46,10 +58,7 @@ var TokenManager = /*#__PURE__*/function () {
46
58
  activeTokenRefresh = _tokenDetail.activeTokenRefresh;
47
59
 
48
60
  if (mediaApiToken) {
49
- var nowInSeconds = Date.now() / 1000;
50
- var expiresAt = mediaApiToken.expiresAt - expireAdjustment;
51
-
52
- if (nowInSeconds < expiresAt && !forceRefresh) {
61
+ if (this.isValidToken(mediaApiToken) && !forceRefresh) {
53
62
  // still valid
54
63
  return Promise.resolve(mediaApiToken);
55
64
  }
@@ -29,8 +29,6 @@ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/h
29
29
 
30
30
  var _react = _interopRequireWildcard(require("react"));
31
31
 
32
- var _EmojiUtils = require("../../api/EmojiUtils");
33
-
34
32
  var _typeHelpers = require("../../util/type-helpers");
35
33
 
36
34
  var _logger = _interopRequireDefault(require("../../util/logger"));
@@ -101,55 +99,43 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
101
99
 
102
100
  (0, _classCallCheck2.default)(this, CachingMediaEmoji);
103
101
  _this = _super.call(this, props);
104
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "mounted", false);
105
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleLoadError", function (_emojiId, emoji) {
106
- var invalidImage = _this.state.invalidImage;
102
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleLoadError", function (_emojiId) {
107
103
  (0, _analytics.sampledUfoRenderedEmoji)(_emojiId).failure({
108
104
  metadata: {
109
105
  reason: 'load error'
110
106
  }
111
107
  });
112
108
 
113
- if (invalidImage || !emoji) {
114
- // do nothing, bad image
115
- return;
116
- }
117
-
118
109
  _this.setState({
119
- cachedEmoji: _this.loadEmoji(emoji, _this.context, true)
110
+ invalidImage: true
120
111
  });
121
112
  });
122
113
  _this.state = {
123
- cachedEmoji: _this.loadEmoji(props.emoji, context, false)
114
+ cachedEmoji: undefined
124
115
  };
116
+
117
+ _this.loadEmoji(props.emoji, context);
118
+
125
119
  return _this;
126
120
  }
127
121
 
128
122
  (0, _createClass2.default)(CachingMediaEmoji, [{
129
123
  key: "componentDidMount",
130
124
  value: function componentDidMount() {
131
- this.mounted = true;
132
125
  (0, _analytics.sampledUfoRenderedEmoji)(this.props.emoji).markFMP();
133
126
  }
134
127
  }, {
135
- key: "componentWillUnmount",
136
- value: function componentWillUnmount() {
137
- this.mounted = false;
138
- }
139
- }, {
140
- key: "UNSAFE_componentWillReceiveProps",
141
- value: function UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
142
- if (nextProps.emoji !== this.props.emoji) {
143
- if (this.mounted) {
144
- this.setState({
145
- cachedEmoji: this.loadEmoji(nextProps.emoji, nextContext, false)
146
- });
147
- }
128
+ key: "componentDidUpdate",
129
+ value: function componentDidUpdate() {
130
+ var _this$state$cachedEmo;
131
+
132
+ if (this.props.emoji.shortName !== ((_this$state$cachedEmo = this.state.cachedEmoji) === null || _this$state$cachedEmo === void 0 ? void 0 : _this$state$cachedEmo.shortName)) {
133
+ this.loadEmoji(this.props.emoji, this.context);
148
134
  }
149
135
  }
150
136
  }, {
151
137
  key: "loadEmoji",
152
- value: function loadEmoji(emoji, context, forceLoad) {
138
+ value: function loadEmoji(emoji, context) {
153
139
  var _this2 = this;
154
140
 
155
141
  if (!context) {
@@ -166,60 +152,38 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
166
152
  return undefined;
167
153
  }
168
154
 
169
- var fitToHeight = this.props.fitToHeight;
170
- var useAlt = (0, _EmojiUtils.shouldUseAltRepresentation)(emoji, fitToHeight);
171
- var optimisticRendering = emojiProvider.optimisticMediaRendering(emoji, useAlt);
172
-
173
- if (optimisticRendering && !forceLoad) {
174
- (0, _logger.default)('Optimistic rendering', emoji.shortName);
175
- return emoji;
176
- }
177
-
178
155
  (0, _logger.default)('Loading image via media cache', emoji.shortName);
179
- var loadedEmoji = emojiProvider.loadMediaEmoji(emoji, useAlt);
180
-
181
- if ((0, _typeHelpers.isPromise)(loadedEmoji)) {
182
- loadedEmoji.then(function (cachedEmoji) {
183
- if (_this2.mounted) {
184
- _this2.setState({
185
- cachedEmoji: cachedEmoji,
186
- invalidImage: !cachedEmoji
187
- });
188
- }
189
- }).catch(function () {
190
- if (_this2.mounted) {
191
- _this2.setState({
192
- cachedEmoji: undefined,
193
- invalidImage: true
194
- });
195
-
196
- (0, _analytics.sampledUfoRenderedEmoji)(emoji).failure({
197
- metadata: {
198
- reason: 'failed to load media emoji'
199
- }
200
- });
201
- }
156
+ emojiProvider.getMediaEmojiDescriptionURLWithInlineToken(emoji).then(function (cachedEmoji) {
157
+ _this2.setState({
158
+ cachedEmoji: cachedEmoji,
159
+ invalidImage: false
160
+ });
161
+ }).catch(function () {
162
+ _this2.setState({
163
+ cachedEmoji: undefined,
164
+ invalidImage: true
202
165
  });
203
- return undefined;
204
- }
205
-
206
- if ((0, _typeHelpers.isEmojiDescription)(loadedEmoji)) {
207
- return loadedEmoji;
208
- }
209
166
 
210
- return undefined;
167
+ (0, _analytics.sampledUfoRenderedEmoji)(emoji).failure({
168
+ metadata: {
169
+ reason: 'failed to load media emoji'
170
+ }
171
+ });
172
+ });
211
173
  }
212
174
  }, {
213
175
  key: "render",
214
176
  value: function render() {
215
- var cachedEmoji = this.state.cachedEmoji;
177
+ var _this$state = this.state,
178
+ cachedEmoji = _this$state.cachedEmoji,
179
+ invalidImage = _this$state.invalidImage;
216
180
  var _this$props = this.props,
217
181
  children = _this$props.children,
218
182
  placeholderSize = _this$props.placeholderSize,
219
183
  otherProps = (0, _objectWithoutProperties2.default)(_this$props, _excluded2);
220
184
  var emojiComponent;
221
185
 
222
- if (cachedEmoji) {
186
+ if (cachedEmoji && !invalidImage) {
223
187
  emojiComponent = /*#__PURE__*/_react.default.createElement(_Emoji.default, (0, _extends2.default)({}, otherProps, {
224
188
  emoji: cachedEmoji,
225
189
  onLoadError: this.handleLoadError
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/emoji",
3
- "version": "64.5.0",
3
+ "version": "64.6.0",
4
4
  "sideEffects": false
5
5
  }
@@ -1,7 +1,7 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { AbstractResource, utils as serviceUtils } from '@atlaskit/util-service-support';
3
3
  import { selectedToneStorageKey } from '../util/constants';
4
- import { isMediaEmoji, isPromise, toEmojiId } from '../util/type-helpers';
4
+ import { isMediaEmoji, isMediaRepresentation, isPromise, toEmojiId } from '../util/type-helpers';
5
5
  import storageAvailable from '../util/storage-available';
6
6
  import { ProviderTypes } from '../types';
7
7
  import debug from '../util/logger';
@@ -15,14 +15,11 @@ import { ufoExperiences } from '../util/analytics';
15
15
  *
16
16
  * Follow this up with an isUploadSupported() check to see if the provider is actually
17
17
  * configured to support uploads.
18
+ * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
18
19
  */
19
20
  export const supportsUploadFeature = emojiProvider => {
20
- const {
21
- isUploadSupported,
22
- prepareForUpload,
23
- uploadCustomEmoji
24
- } = emojiProvider;
25
- return !!(isUploadSupported && prepareForUpload && uploadCustomEmoji);
21
+ const emojiUploadProvider = emojiProvider;
22
+ return !!emojiUploadProvider.isUploadSupported && !!emojiUploadProvider.uploadCustomEmoji && !!emojiUploadProvider.prepareForUpload;
26
23
  };
27
24
  export class EmojiResource extends AbstractResource {
28
25
  constructor(config) {
@@ -184,6 +181,23 @@ export class EmojiResource extends AbstractResource {
184
181
  super.notifyResult(result);
185
182
  }
186
183
  }
184
+ /**
185
+ * Returns the EmojiDescription with a valid media path that includes query token and client attributes to access the emoji media inline.
186
+ */
187
+
188
+
189
+ async getMediaEmojiDescriptionURLWithInlineToken(emoji) {
190
+ if (this.siteEmojiResource && isMediaRepresentation(emoji.representation)) {
191
+ const tokenisedMediaPath = await this.siteEmojiResource.generateTokenisedMediaURL(emoji);
192
+ return { ...emoji,
193
+ representation: { ...emoji.representation,
194
+ mediaPath: tokenisedMediaPath
195
+ }
196
+ };
197
+ }
198
+
199
+ return emoji;
200
+ }
187
201
 
188
202
  loadMediaEmoji(emoji, useAlt) {
189
203
  if (!this.siteEmojiResource || !isMediaEmoji(emoji)) {
@@ -63,6 +63,30 @@ export default class SiteEmojiResource {
63
63
  this.tokenManager.addToken('read', mediaApiToken);
64
64
  this.mediaEmojiCache = new MediaEmojiCache(this.tokenManager);
65
65
  }
66
+ /**
67
+ * Will generate an emoji media path that is inclusive of client and token within the query parameter
68
+ */
69
+
70
+
71
+ async generateTokenisedMediaURL(emoji) {
72
+ if (emoji && isMediaRepresentation(emoji.representation)) {
73
+ const currentMediaPathURL = new URL(emoji.representation.mediaPath);
74
+ const currentMediaPathPARAMS = currentMediaPathURL.searchParams;
75
+ const readToken = await this.tokenManager.getToken('read');
76
+
77
+ if (currentMediaPathPARAMS.get('token') !== readToken.jwt) {
78
+ currentMediaPathPARAMS.set('token', readToken.jwt);
79
+ }
80
+
81
+ if (currentMediaPathPARAMS.get('client') !== readToken.clientId) {
82
+ currentMediaPathPARAMS.set('client', readToken.clientId);
83
+ }
84
+
85
+ return currentMediaPathURL.toString();
86
+ }
87
+
88
+ throw Error('Emoji resource is not of type Media Representation');
89
+ }
66
90
  /**
67
91
  * Will load media emoji, returning a new EmojiDescription if, for example,
68
92
  * the URL has changed.
@@ -1,12 +1,23 @@
1
1
  import { utils as serviceUtils } from '@atlaskit/util-service-support'; // expire 30 seconds early to factor in latency, slow services, etc
2
2
 
3
- export const expireAdjustment = 30;
3
+ export const EXPIRES_AT_LATENCY_IN_SECONDS = 30;
4
4
  export default class TokenManager {
5
5
  constructor(siteServiceConfig) {
6
6
  this.siteServiceConfig = siteServiceConfig;
7
7
  this.tokens = new Map();
8
8
  }
9
9
 
10
+ isValidToken(mediaApiToken) {
11
+ const nowInSeconds = Date.now() / 1000;
12
+ const expiresAt = mediaApiToken.expiresAt - EXPIRES_AT_LATENCY_IN_SECONDS;
13
+
14
+ if (nowInSeconds < expiresAt) {
15
+ return true;
16
+ }
17
+
18
+ return false;
19
+ }
20
+
10
21
  addToken(type, mediaApiToken) {
11
22
  this.tokens.set(type, {
12
23
  mediaApiToken
@@ -27,10 +38,7 @@ export default class TokenManager {
27
38
  } = tokenDetail;
28
39
 
29
40
  if (mediaApiToken) {
30
- const nowInSeconds = Date.now() / 1000;
31
- const expiresAt = mediaApiToken.expiresAt - expireAdjustment;
32
-
33
- if (nowInSeconds < expiresAt && !forceRefresh) {
41
+ if (this.isValidToken(mediaApiToken) && !forceRefresh) {
34
42
  // still valid
35
43
  return Promise.resolve(mediaApiToken);
36
44
  }