@atlaskit/smart-card 44.7.0 → 44.7.1

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 (59) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-cold-cache--default.png +3 -0
  3. package/dist/cjs/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-message-low-tier--default.png +3 -0
  4. package/dist/cjs/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-message-not-low-tier--default.png +3 -0
  5. package/dist/cjs/messages.js +12 -1
  6. package/dist/cjs/state/hooks/use-current-site-cloud-id/index.js +0 -30
  7. package/dist/cjs/state/hooks/use-incoming-outgoing-links/index.js +3 -3
  8. package/dist/cjs/state/hooks/use-social-proof/index.js +44 -28
  9. package/dist/cjs/state/hooks/use-social-proof-experiment/index.js +48 -0
  10. package/dist/cjs/state/services/current-site-cloud-id/index.js +61 -74
  11. package/dist/cjs/state/services/personalization/index.js +59 -60
  12. package/dist/cjs/utils/analytics/analytics.js +1 -1
  13. package/dist/cjs/view/BlockCard/views/SocialProofMessage.js +36 -0
  14. package/dist/cjs/view/BlockCard/views/UnauthorisedView.js +85 -20
  15. package/dist/cjs/view/LinkUrl/index.js +1 -1
  16. package/dist/es2019/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-cold-cache--default.png +3 -0
  17. package/dist/es2019/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-message-low-tier--default.png +3 -0
  18. package/dist/es2019/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-message-not-low-tier--default.png +3 -0
  19. package/dist/es2019/messages.js +12 -1
  20. package/dist/es2019/state/hooks/use-current-site-cloud-id/index.js +0 -1
  21. package/dist/es2019/state/hooks/use-incoming-outgoing-links/index.js +2 -2
  22. package/dist/es2019/state/hooks/use-social-proof/index.js +41 -22
  23. package/dist/es2019/state/hooks/use-social-proof-experiment/index.js +41 -0
  24. package/dist/es2019/state/services/current-site-cloud-id/index.js +43 -67
  25. package/dist/es2019/state/services/personalization/index.js +40 -39
  26. package/dist/es2019/utils/analytics/analytics.js +1 -1
  27. package/dist/es2019/view/BlockCard/views/SocialProofMessage.js +27 -0
  28. package/dist/es2019/view/BlockCard/views/UnauthorisedView.js +83 -15
  29. package/dist/es2019/view/LinkUrl/index.js +1 -1
  30. package/dist/esm/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-cold-cache--default.png +3 -0
  31. package/dist/esm/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-message-low-tier--default.png +3 -0
  32. package/dist/esm/__tests__/vr-tests/__snapshots__/block-card/block-card-social-proof-message-not-low-tier--default.png +3 -0
  33. package/dist/esm/messages.js +12 -1
  34. package/dist/esm/state/hooks/use-current-site-cloud-id/index.js +0 -1
  35. package/dist/esm/state/hooks/use-incoming-outgoing-links/index.js +2 -2
  36. package/dist/esm/state/hooks/use-social-proof/index.js +45 -29
  37. package/dist/esm/state/hooks/use-social-proof-experiment/index.js +41 -0
  38. package/dist/esm/state/services/current-site-cloud-id/index.js +59 -72
  39. package/dist/esm/state/services/personalization/index.js +56 -59
  40. package/dist/esm/utils/analytics/analytics.js +1 -1
  41. package/dist/esm/view/BlockCard/views/SocialProofMessage.js +29 -0
  42. package/dist/esm/view/BlockCard/views/UnauthorisedView.js +85 -20
  43. package/dist/esm/view/LinkUrl/index.js +1 -1
  44. package/dist/types/messages.d.ts +1 -1
  45. package/dist/types/state/hooks/use-current-site-cloud-id/index.d.ts +0 -1
  46. package/dist/types/state/hooks/use-social-proof/index.d.ts +17 -4
  47. package/dist/types/state/hooks/use-social-proof-experiment/index.d.ts +39 -0
  48. package/dist/types/state/services/current-site-cloud-id/index.d.ts +9 -33
  49. package/dist/types/state/services/personalization/index.d.ts +14 -24
  50. package/dist/types/view/BlockCard/views/SocialProofMessage.d.ts +14 -0
  51. package/dist/types-ts4.5/messages.d.ts +1 -1
  52. package/dist/types-ts4.5/state/hooks/use-current-site-cloud-id/index.d.ts +0 -1
  53. package/dist/types-ts4.5/state/hooks/use-social-proof/index.d.ts +17 -4
  54. package/dist/types-ts4.5/state/hooks/use-social-proof-experiment/index.d.ts +39 -0
  55. package/dist/types-ts4.5/state/services/current-site-cloud-id/index.d.ts +9 -33
  56. package/dist/types-ts4.5/state/services/personalization/index.d.ts +14 -24
  57. package/dist/types-ts4.5/view/BlockCard/views/SocialProofMessage.d.ts +14 -0
  58. package/package.json +8 -2
  59. package/smart-card.docs.tsx +2 -2
@@ -4,9 +4,11 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.PersonalizationService = exports.PERSONALIZATION_STORAGE_SCOPE = exports.PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = void 0;
7
+ exports.SOCIAL_PROOF_TRAIT_NAME = exports.PersonalizationService = exports.PERSONALIZATION_STORAGE_SCOPE = exports.PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = exports.PERSONALIZATION_PROVIDER_PCT_TTL_MS = void 0;
8
8
  exports.getCachedProviderPctMapAndRefresh = getCachedProviderPctMapAndRefresh;
9
- exports.personalizationService = exports.getProviderPctMap = void 0;
9
+ exports.getProviderPctMap = void 0;
10
+ exports.getProviderPctMapSync = getProviderPctMapSync;
11
+ exports.personalizationService = void 0;
10
12
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
13
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
12
14
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
@@ -17,19 +19,17 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
17
19
  var _storageClient = require("@atlaskit/frontend-utilities/storage-client");
18
20
  var _currentSiteCloudId = require("../current-site-cloud-id");
19
21
  var BASE_URL = '/gateway/api/tap-delivery/api/v3/personalization';
20
-
21
- /**
22
- * Logical key shape: `@atlaskit/smart-card:<feature>:<schema-version>:<scope>` (see smart-card
23
- * storage conventions). {@link StorageClient} narrows the localStorage key to
24
- * `<clientKey>_<itemKey>` with `clientKey === '@atlaskit/smart-card'` and
25
- * `itemKey === 'pct-map:v1:<cloudId>:<traitName>'` (scope segments URI-encoded).
26
- */
27
- var PERSONALIZATION_STORAGE_SCOPE = exports.PERSONALIZATION_STORAGE_SCOPE = '@atlaskit/smart-card';
22
+ var PERSONALIZATION_STORAGE_SCOPE = exports.PERSONALIZATION_STORAGE_SCOPE = 'smart-card-social-proof';
28
23
  var PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = exports.PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = 'pct-map:v1:';
24
+ var PERSONALIZATION_PROVIDER_PCT_TTL_MS = exports.PERSONALIZATION_PROVIDER_PCT_TTL_MS = 24 * 60 * 60 * 1000;
25
+ var SOCIAL_PROOF_TRAIT_NAME = exports.SOCIAL_PROOF_TRAIT_NAME = 'sl_3p_connected_providers_site_pct';
29
26
  var smartCardStorage = new _storageClient.StorageClient(PERSONALIZATION_STORAGE_SCOPE);
30
27
 
31
28
  /** Keys written by this service in localStorage when using {@link smartCardStorage}. */
32
29
  var LOCAL_STORAGE_ROW_KEY_PREFIX = "".concat(PERSONALIZATION_STORAGE_SCOPE, "_").concat(PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX);
30
+ function scopedCacheKey(cloudId, traitName) {
31
+ return "".concat(cloudId, ":").concat(traitName);
32
+ }
33
33
  function pctMapStorageItemKey(cloudId, traitName) {
34
34
  return "".concat(PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX).concat(encodeURIComponent(cloudId), ":").concat(encodeURIComponent(traitName));
35
35
  }
@@ -81,53 +81,46 @@ var PersonalizationService = exports.PersonalizationService = /*#__PURE__*/funct
81
81
  (0, _defineProperty2.default)(this, "cache", new Map());
82
82
  }
83
83
  return (0, _createClass2.default)(PersonalizationService, [{
84
- key: "getCachedProviderPctMapAndRefresh",
85
- value:
86
- /**
87
- * Returns the currently cached provider percentage map synchronously and starts a background refresh.
88
- * The refresh result is persisted for future calls but is not awaited by this call.
89
- */
90
- function getCachedProviderPctMapAndRefresh(traitName) {
91
- var cloudId = (0, _currentSiteCloudId.getCachedCurrentSiteCloudIdAndRefresh)();
92
- var fromStorage = cloudId && this.readStoredProviderPctMap(cloudId, traitName) || null;
93
- void this.getProviderPctMap(traitName);
94
- return fromStorage;
84
+ key: "getProviderPctMapSync",
85
+ value: /** Pure synchronous read for an explicit cloud id / trait pair. */
86
+ function getProviderPctMapSync(cloudId, traitName) {
87
+ if (!cloudId) {
88
+ return null;
89
+ }
90
+ return this.readStoredProviderPctMap(cloudId, traitName);
95
91
  }
96
92
  }, {
97
93
  key: "getProviderPctMap",
98
94
  value: function () {
99
- var _getProviderPctMap = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(traitName) {
95
+ var _getProviderPctMap = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(cloudId, traitName) {
100
96
  var _this = this;
101
- var cachedPromise, promise;
97
+ var cacheKey, cachedPromise, promise, retryablePromise;
102
98
  return _regenerator.default.wrap(function _callee2$(_context2) {
103
99
  while (1) switch (_context2.prev = _context2.next) {
104
100
  case 0:
105
- cachedPromise = this.cache.get(traitName);
101
+ if (cloudId) {
102
+ _context2.next = 2;
103
+ break;
104
+ }
105
+ return _context2.abrupt("return", undefined);
106
+ case 2:
107
+ cacheKey = scopedCacheKey(cloudId, traitName);
108
+ cachedPromise = this.cache.get(cacheKey);
106
109
  if (!cachedPromise) {
107
- _context2.next = 3;
110
+ _context2.next = 6;
108
111
  break;
109
112
  }
110
113
  return _context2.abrupt("return", cachedPromise);
111
- case 3:
114
+ case 6:
112
115
  promise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
113
- var cloudId, traits, trait, mapped;
116
+ var traits, trait, mapped;
114
117
  return _regenerator.default.wrap(function _callee$(_context) {
115
118
  while (1) switch (_context.prev = _context.next) {
116
119
  case 0:
117
120
  _context.prev = 0;
118
121
  _context.next = 3;
119
- return (0, _currentSiteCloudId.getCurrentSiteCloudId)();
120
- case 3:
121
- cloudId = _context.sent;
122
- if (cloudId) {
123
- _context.next = 6;
124
- break;
125
- }
126
- return _context.abrupt("return", undefined);
127
- case 6:
128
- _context.next = 8;
129
122
  return fetchSiteTraits(cloudId);
130
- case 8:
123
+ case 3:
131
124
  traits = _context.sent;
132
125
  trait = traits.find(function (t) {
133
126
  return t.name === traitName;
@@ -137,25 +130,28 @@ var PersonalizationService = exports.PersonalizationService = /*#__PURE__*/funct
137
130
  _this.writeStoredProviderPctMap(cloudId, traitName, mapped);
138
131
  }
139
132
  return _context.abrupt("return", mapped);
140
- case 15:
141
- _context.prev = 15;
133
+ case 10:
134
+ _context.prev = 10;
142
135
  _context.t0 = _context["catch"](0);
143
136
  return _context.abrupt("return", undefined);
144
- case 18:
137
+ case 13:
145
138
  case "end":
146
139
  return _context.stop();
147
140
  }
148
- }, _callee, null, [[0, 15]]);
141
+ }, _callee, null, [[0, 10]]);
149
142
  }))();
150
- this.cache.set(traitName, promise);
151
- return _context2.abrupt("return", promise);
152
- case 6:
143
+ retryablePromise = promise.finally(function () {
144
+ _this.cache.delete(cacheKey);
145
+ });
146
+ this.cache.set(cacheKey, retryablePromise);
147
+ return _context2.abrupt("return", retryablePromise);
148
+ case 10:
153
149
  case "end":
154
150
  return _context2.stop();
155
151
  }
156
152
  }, _callee2, this);
157
153
  }));
158
- function getProviderPctMap(_x2) {
154
+ function getProviderPctMap(_x2, _x3) {
159
155
  return _getProviderPctMap.apply(this, arguments);
160
156
  }
161
157
  return getProviderPctMap;
@@ -177,7 +173,7 @@ var PersonalizationService = exports.PersonalizationService = /*#__PURE__*/funct
177
173
  key: "writeStoredProviderPctMap",
178
174
  value: function writeStoredProviderPctMap(cloudId, traitName, map) {
179
175
  try {
180
- smartCardStorage.setItemWithExpiry(pctMapStorageItemKey(cloudId, traitName), map);
176
+ smartCardStorage.setItemWithExpiry(pctMapStorageItemKey(cloudId, traitName), map, PERSONALIZATION_PROVIDER_PCT_TTL_MS);
181
177
  } catch (_unused3) {
182
178
  // Quota, private-mode, etc.
183
179
  }
@@ -246,22 +242,25 @@ var PersonalizationService = exports.PersonalizationService = /*#__PURE__*/funct
246
242
  }]);
247
243
  }();
248
244
  var personalizationService = exports.personalizationService = new PersonalizationService();
249
-
250
- /**
251
- * Resolves the provider percentage map for a TAP Delivery trait through the module-level
252
- * {@link personalizationService}. Work is deduped per trait name for the page lifetime, and a
253
- * successful response is persisted by cloud id and trait name for later cached reads.
254
- */
255
- var getProviderPctMap = exports.getProviderPctMap = function getProviderPctMap(traitName) {
256
- return personalizationService.getProviderPctMap(traitName);
245
+ var getProviderPctMap = exports.getProviderPctMap = function getProviderPctMap(cloudId, traitName) {
246
+ return personalizationService.getProviderPctMap(cloudId, traitName);
257
247
  };
248
+ function getProviderPctMapSync(cloudId, traitName) {
249
+ return personalizationService.getProviderPctMapSync(cloudId, traitName);
250
+ }
258
251
 
259
252
  /**
260
- * Reads the provider percentage map for a trait from browser storage via the module-level
261
- * {@link personalizationService} singleton, without awaiting network work.
262
- * Calling this also starts the trait-scoped shared refresh in the background, so a later call can
263
- * use a refreshed value when it becomes available.
253
+ * Backwards-compatible cache-first helper for inline-card social proof callers.
254
+ *
255
+ * Reads the persisted provider percentage map synchronously using the current site cloud id, then
256
+ * starts a background refresh for subsequent mounts. The async result intentionally does not affect
257
+ * the current call site, matching the warm-cache-only rendering contract.
264
258
  */
265
259
  function getCachedProviderPctMapAndRefresh(traitName) {
266
- return personalizationService.getCachedProviderPctMapAndRefresh(traitName);
260
+ var cloudId = (0, _currentSiteCloudId.getCurrentSiteCloudIdSync)();
261
+ var providerPctMap = getProviderPctMapSync(cloudId, traitName);
262
+ void (0, _currentSiteCloudId.getCurrentSiteCloudId)().then(function (resolvedCloudId) {
263
+ void getProviderPctMap(resolvedCloudId, traitName);
264
+ });
265
+ return providerPctMap;
267
266
  }
@@ -11,7 +11,7 @@ var ANALYTICS_CHANNEL = exports.ANALYTICS_CHANNEL = 'media';
11
11
  var context = exports.context = {
12
12
  componentName: 'smart-cards',
13
13
  packageName: "@atlaskit/smart-card" || '',
14
- packageVersion: "44.6.1" || ''
14
+ packageVersion: "44.7.0" || ''
15
15
  };
16
16
  var TrackQuickActionType = exports.TrackQuickActionType = /*#__PURE__*/function (TrackQuickActionType) {
17
17
  TrackQuickActionType["StatusUpdate"] = "StatusUpdate";
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
+ var _react = _interopRequireDefault(require("react"));
10
+ var _reactIntl = require("react-intl");
11
+ var _compiled = require("@atlaskit/primitives/compiled");
12
+ var _messages = require("../../../messages");
13
+ // TODO: remove when social-proof-3p-unauth-block-fg is cleaned up
14
+ var SocialProofMessage = function SocialProofMessage(_ref) {
15
+ var tier = _ref.tier,
16
+ connectedPct = _ref.connectedPct,
17
+ providerName = _ref.providerName,
18
+ _ref$testId = _ref.testId,
19
+ testId = _ref$testId === void 0 ? 'smart-block-social-proof-message' : _ref$testId;
20
+ var message = tier === 'not-low' ? _messages.messages.pre_auth_block_social_proof_not_low : _messages.messages.pre_auth_block_social_proof_low;
21
+ return /*#__PURE__*/_react.default.createElement(_compiled.Box, {
22
+ testId: testId
23
+ }, /*#__PURE__*/_react.default.createElement(_reactIntl.FormattedMessage, (0, _extends2.default)({}, message, {
24
+ values: {
25
+ percentage: connectedPct !== null && connectedPct !== void 0 ? connectedPct : 0,
26
+ provider: providerName,
27
+ b: function b(chunks) {
28
+ return /*#__PURE__*/_react.default.createElement(_compiled.Text, {
29
+ as: "strong",
30
+ weight: "bold"
31
+ }, chunks);
32
+ }
33
+ }
34
+ })));
35
+ };
36
+ var _default = exports.default = SocialProofMessage;
@@ -14,13 +14,18 @@ var _runtime = require("@compiled/react/runtime");
14
14
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
15
15
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
16
16
  var _reactIntl = require("react-intl");
17
+ var _reactMagneticDi = require("react-magnetic-di");
18
+ var _analyticsNext = require("@atlaskit/analytics-next");
17
19
  var _linkExtractors = require("@atlaskit/link-extractors");
20
+ var _linkProvider = require("@atlaskit/link-provider");
21
+ var _platformFeatureFlagsReact = require("@atlaskit/platform-feature-flags-react");
18
22
  var _compiled = require("@atlaskit/primitives/compiled");
19
23
  var _useAnalyticsEvents2 = require("../../../common/analytics/generated/use-analytics-events");
20
24
  var _constants = require("../../../constants");
21
25
  var _messages = require("../../../messages");
22
26
  var _flexibleUiContext = require("../../../state/flexible-ui-context");
23
27
  var _helpers = require("../../../state/helpers");
28
+ var _useSocialProofExperiment = _interopRequireDefault(require("../../../state/hooks/use-social-proof-experiment"));
24
29
  var _UnauthorisedViewContent = _interopRequireDefault(require("../../common/UnauthorisedViewContent"));
25
30
  var _FlexibleCard = _interopRequireDefault(require("../../FlexibleCard"));
26
31
  var _actionGroup = _interopRequireDefault(require("../../FlexibleCard/components/blocks/action-group"));
@@ -30,9 +35,12 @@ var _utils = require("../../FlexibleCard/components/blocks/utils");
30
35
  var _elements = require("../../FlexibleCard/components/elements");
31
36
  var _AuthorizeAction = require("../actions/AuthorizeAction");
32
37
  var _general2x = _interopRequireDefault(require("./assets/general@2x.png"));
38
+ var _SocialProofMessage = _interopRequireDefault(require("./SocialProofMessage"));
33
39
  var _utils2 = require("./utils");
34
40
  var _withFlexibleUIBlockCardStyle = require("./utils/withFlexibleUIBlockCardStyle");
35
- var _excluded = ["testId"];
41
+ var _excluded = ["content", "providerName", "testId"],
42
+ _excluded2 = ["testId"],
43
+ _excluded3 = ["testId"];
36
44
  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); }
37
45
  var contentStyles = null;
38
46
  var contentAdditionalStyles = null;
@@ -170,15 +178,12 @@ var UnauthorisedBlock = function UnauthorisedBlock(_ref) {
170
178
  * @see SmartLinkStatus
171
179
  * @see FlexibleCardProps
172
180
  */
173
- var UnauthorisedView = function UnauthorisedView(_ref3) {
174
- var _extractSmartLinkProv;
175
- var _ref3$testId = _ref3.testId,
176
- testId = _ref3$testId === void 0 ? 'smart-block-unauthorized-view' : _ref3$testId,
181
+ var UnauthorisedViewFrame = function UnauthorisedViewFrame(_ref3) {
182
+ var content = _ref3.content,
183
+ providerName = _ref3.providerName,
184
+ testId = _ref3.testId,
177
185
  props = (0, _objectWithoutProperties2.default)(_ref3, _excluded);
178
- var cardState = props.cardState,
179
- onAuthorize = props.onAuthorize;
180
- var providerName = (_extractSmartLinkProv = (0, _linkExtractors.extractSmartLinkProvider)(cardState.details)) === null || _extractSmartLinkProv === void 0 ? void 0 : _extractSmartLinkProv.text;
181
- var isProductIntegrationSupported = (0, _helpers.hasAuthScopeOverrides)(cardState.details);
186
+ var onAuthorize = props.onAuthorize;
182
187
  var _useAnalyticsEvents = (0, _useAnalyticsEvents2.useAnalyticsEvents)(),
183
188
  fireEvent = _useAnalyticsEvents.fireEvent;
184
189
  var handleAuthorize = (0, _react.useCallback)(function () {
@@ -187,17 +192,6 @@ var UnauthorisedView = function UnauthorisedView(_ref3) {
187
192
  onAuthorize();
188
193
  }
189
194
  }, [onAuthorize, fireEvent]);
190
- var content = (0, _react.useMemo)(function () {
191
- return onAuthorize ? /*#__PURE__*/React.createElement(_UnauthorisedViewContent.default, {
192
- providerName: providerName,
193
- isProductIntegrationSupported: isProductIntegrationSupported,
194
- testId: testId
195
- }) : /*#__PURE__*/React.createElement(_reactIntl.FormattedMessage, (0, _extends2.default)({}, _messages.messages[providerName ? 'unauthorised_account_description' : 'unauthorised_account_description_no_provider'], {
196
- values: {
197
- context: providerName
198
- }
199
- }));
200
- }, [isProductIntegrationSupported, onAuthorize, providerName, testId]);
201
195
  var actions = (0, _react.useMemo)(function () {
202
196
  return onAuthorize ? [(0, _AuthorizeAction.AuthorizeAction)(handleAuthorize, providerName)] : [];
203
197
  }, [handleAuthorize, onAuthorize, providerName]);
@@ -218,5 +212,76 @@ var UnauthorisedView = function UnauthorisedView(_ref3) {
218
212
  className: (0, _runtime.ax)(["_11c8fhey _syazi7uo _19pku2gc", actions.length > 0 && "_1reo15vq _18m915vq _otyrpxbi _p12f1osq _o5721q9c _1bto1l2s"])
219
213
  }, content)));
220
214
  };
215
+ var UnauthorisedViewBase = function UnauthorisedViewBase(_ref4) {
216
+ var _extractSmartLinkProv;
217
+ var _ref4$testId = _ref4.testId,
218
+ testId = _ref4$testId === void 0 ? 'smart-block-unauthorized-view' : _ref4$testId,
219
+ props = (0, _objectWithoutProperties2.default)(_ref4, _excluded2);
220
+ var cardState = props.cardState,
221
+ onAuthorize = props.onAuthorize;
222
+ var providerName = (_extractSmartLinkProv = (0, _linkExtractors.extractSmartLinkProvider)(cardState.details)) === null || _extractSmartLinkProv === void 0 ? void 0 : _extractSmartLinkProv.text;
223
+ var isProductIntegrationSupported = (0, _helpers.hasAuthScopeOverrides)(cardState.details);
224
+ var content = (0, _react.useMemo)(function () {
225
+ return onAuthorize ? /*#__PURE__*/React.createElement(_UnauthorisedViewContent.default, {
226
+ providerName: providerName,
227
+ isProductIntegrationSupported: isProductIntegrationSupported,
228
+ testId: testId
229
+ }) : /*#__PURE__*/React.createElement(_reactIntl.FormattedMessage, (0, _extends2.default)({}, _messages.messages[providerName ? 'unauthorised_account_description' : 'unauthorised_account_description_no_provider'], {
230
+ values: {
231
+ context: providerName
232
+ }
233
+ }));
234
+ }, [isProductIntegrationSupported, onAuthorize, providerName, testId]);
235
+ return /*#__PURE__*/React.createElement(UnauthorisedViewFrame, (0, _extends2.default)({}, props, {
236
+ content: content,
237
+ providerName: providerName,
238
+ testId: testId
239
+ }));
240
+ };
241
+
242
+ /**
243
+ * Experiment wrapper: fires social proof exposure and renders social proof UI when FG is on.
244
+ * TODO: remove when social-proof-3p-unauth-block-fg is cleaned up
245
+ */
246
+ var UnauthorisedViewWithExperiment = function UnauthorisedViewWithExperiment(_ref5) {
247
+ var _props$cardState, _extractSmartLinkProv2;
248
+ var _ref5$testId = _ref5.testId,
249
+ testId = _ref5$testId === void 0 ? 'smart-block-unauthorized-view' : _ref5$testId,
250
+ props = (0, _objectWithoutProperties2.default)(_ref5, _excluded3);
251
+ var extensionKey = (_props$cardState = props.cardState) === null || _props$cardState === void 0 || (_props$cardState = _props$cardState.details) === null || _props$cardState === void 0 || (_props$cardState = _props$cardState.meta) === null || _props$cardState === void 0 ? void 0 : _props$cardState.key;
252
+ var providerName = (_extractSmartLinkProv2 = (0, _linkExtractors.extractSmartLinkProvider)(props.cardState.details)) === null || _extractSmartLinkProv2 === void 0 ? void 0 : _extractSmartLinkProv2.text;
253
+ var _useSmartLinkContext = (0, _linkProvider.useSmartLinkContext)(),
254
+ connections = _useSmartLinkContext.connections;
255
+ var _useSocialProofExperi = (0, _useSocialProofExperiment.default)(providerName ? extensionKey : undefined, connections.client.baseUrlOverride),
256
+ isTreatment = _useSocialProofExperi.isTreatment,
257
+ tier = _useSocialProofExperi.tier,
258
+ connectedPct = _useSocialProofExperi.connectedPct;
259
+ if (!isTreatment || !providerName) {
260
+ return /*#__PURE__*/React.createElement(UnauthorisedViewBase, (0, _extends2.default)({}, props, {
261
+ testId: testId
262
+ }));
263
+ }
264
+ return /*#__PURE__*/React.createElement(_analyticsNext.AnalyticsContext, {
265
+ data: {
266
+ attributes: {
267
+ experiment: 'social_proof_3p_unauth_block_exp',
268
+ cohort: 'treatment',
269
+ tier: tier
270
+ }
271
+ }
272
+ }, /*#__PURE__*/React.createElement(UnauthorisedViewFrame, (0, _extends2.default)({}, props, {
273
+ content: /*#__PURE__*/React.createElement(_SocialProofMessage.default, {
274
+ tier: tier,
275
+ connectedPct: connectedPct,
276
+ providerName: providerName
277
+ }),
278
+ providerName: providerName,
279
+ testId: testId
280
+ })));
281
+ };
282
+ var UnauthorisedViewWithoutExperiment = function UnauthorisedViewWithoutExperiment(props) {
283
+ return /*#__PURE__*/React.createElement(UnauthorisedViewBase, props);
284
+ };
285
+ var UnauthorisedView = (0, _platformFeatureFlagsReact.componentWithFG)('social-proof-3p-unauth-block-fg', UnauthorisedViewWithExperiment, UnauthorisedViewWithoutExperiment);
221
286
  var _default_1 = (0, _withFlexibleUIBlockCardStyle.withFlexibleUIBlockCardStyle)(UnauthorisedView);
222
287
  var _default = exports.default = _default_1;
@@ -22,7 +22,7 @@ var _excluded = ["href", "children", "checkSafety", "onClick", "testId", "isLink
22
22
  _excluded2 = ["isLinkSafe", "showSafetyWarningModal"];
23
23
  var PACKAGE_DATA = {
24
24
  packageName: "@atlaskit/smart-card",
25
- packageVersion: "44.6.1",
25
+ packageVersion: "44.7.0",
26
26
  componentName: 'linkUrl'
27
27
  };
28
28
  var Anchor = (0, _click.withLinkClickedEvent)('a');
@@ -0,0 +1,3 @@
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b072be1d67bf9de1dce752032500f85882f4e3106047ba57d4944e0e9e123c34
3
+ size 17460
@@ -0,0 +1,3 @@
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bc4ec248eb4dfc15dc531155a386a12571398ad5c249de38df839c94cc03c4ab
3
+ size 16074
@@ -0,0 +1,3 @@
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:164d340225092b0d1ac5bd3daf47b305aeeadd0544dc8f76b0fbda83e200572a
3
+ size 16863
@@ -988,7 +988,7 @@ export const messages = defineMessages({
988
988
  },
989
989
  rovo_prompt_message_highlight_relevant_content: {
990
990
  id: 'fabric.linking.rovo_prompt_message_highlight_relevant_content.non-final',
991
- defaultMessage: '<p>Based on this linked item (<a>{url}</a>) and the {context} Im currently viewing, highlight the parts of the linked content that are most relevant to this work. Explain briefly why each part is relevant.</p>',
991
+ defaultMessage: "<p>Based on this linked item (<a>{url}</a>) and the {context} I'm currently viewing, highlight the parts of the linked content that are most relevant to this work. Explain briefly why each part is relevant.</p>",
992
992
  description: 'The prompt message to send to Rovo Chat. {context} refers to the content the user triggered from, e.g. Confluence page or Jira work item. {url} refers to Smart Link that the user triggers this action from. (Please make sure all html tags remain the same.)'
993
993
  },
994
994
  rovo_prompt_button_identify_key_trends: {
@@ -1050,5 +1050,16 @@ export const messages = defineMessages({
1050
1050
  id: 'fabric.linking.rovo_prompt_button_show_me_whats_relevant.non-final',
1051
1051
  defaultMessage: `Show me what's relevant`,
1052
1052
  description: 'The name of the action to send prompt message to Rovo Chat in relation to current Smart Link'
1053
+ },
1054
+ // TODO: remove when social-proof-3p-unauth-block-fg is cleaned up
1055
+ pre_auth_block_social_proof_not_low: {
1056
+ id: 'fabric.linking.pre_auth_block_social_proof_not_low',
1057
+ defaultMessage: '<b>{percentage}%</b> of your team is previewing <b>{provider}</b>.',
1058
+ description: 'Social proof message shown on unauthorized 3P block cards when 30% or more of the tenant has connected the provider. {percentage} is a number, {provider} is the 3P app name (e.g. OneDrive).'
1059
+ },
1060
+ pre_auth_block_social_proof_low: {
1061
+ id: 'fabric.linking.pre_auth_block_social_proof_low',
1062
+ defaultMessage: 'Your team is previewing <b>{provider}</b>.',
1063
+ description: 'Social proof message shown on unauthorized 3P block cards when less than 30% of the tenant has connected the provider. {provider} is the 3P app name (e.g. OneDrive).'
1053
1064
  }
1054
1065
  });
@@ -1,6 +1,5 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { getCurrentSiteCloudId } from '../../services/current-site-cloud-id';
3
- export { CURRENT_SITE_CLOUD_ID_LOCAL_STORAGE_KEY, CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY, currentSiteCloudIdService, getCurrentSiteCloudId, getCachedCurrentSiteCloudIdAndRefresh } from '../../services/current-site-cloud-id';
4
3
  const useCurrentSiteCloudId = () => {
5
4
  const [cloudId, setCloudId] = useState(undefined);
6
5
  const [isLoading, setIsLoading] = useState(true);
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useMemo } from 'react';
2
2
  import { request } from '@atlaskit/linking-common';
3
3
  import { fg } from '@atlaskit/platform-feature-flags';
4
- import { getCurrentSiteCloudId } from '../use-current-site-cloud-id';
4
+ import { getCurrentSiteCloudId } from '../../services/current-site-cloud-id';
5
5
  import { queryIncomingOutgoingLinks as queryIncomingOutgoingAris } from './query';
6
6
  /**
7
7
  * @param baseUriWithNoTrailingSlash base url which will then be appended with /gateway/api/graphql to make requests to AGG
@@ -33,7 +33,7 @@ const useIncomingOutgoingAri = (baseUriWithNoTrailingSlash = '') => {
33
33
  if (match && match[2]) {
34
34
  return match[2]; // Return the cloud_id (siteId)
35
35
  }
36
- return fg('platform_sl_3p_preauth_soc_proof_inline_killswitch') ? await getCurrentSiteCloudId(baseUriWithNoTrailingSlash) : await getCurrentSiteId();
36
+ return fg('platform_sl_incoming_outgoing_tenant_info_killswitch') ? await getCurrentSiteCloudId(baseUriWithNoTrailingSlash) : await getCurrentSiteId();
37
37
  }, [getCurrentSiteId, baseUriWithNoTrailingSlash]);
38
38
  const getIncomingOutgoingAris = useCallback(
39
39
  /**
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useMemo, useState } from 'react';
2
- import { getProviderPctMap } from '../../services/personalization';
2
+ import { getCurrentSiteCloudId, getCurrentSiteCloudIdSync } from '../../services/current-site-cloud-id';
3
+ import { getProviderPctMap, getProviderPctMapSync, SOCIAL_PROOF_TRAIT_NAME } from '../../services/personalization';
3
4
  const NOT_ENABLED_RESULT = {
4
5
  connectedPct: undefined,
5
6
  isEnabled: false,
@@ -7,39 +8,57 @@ const NOT_ENABLED_RESULT = {
7
8
  };
8
9
 
9
10
  /**
10
- * Fetches provider usage percentage from the TAP Delivery personalization service when the
11
- * killswitch allows it. Callers decide separately (e.g. via Statsig experiment) whether to
12
- * surface that data in the UI.
11
+ * Cache-first social proof hook.
12
+ *
13
+ * On mount:
14
+ * 1. Reads localStorage synchronously via `getProviderPctMapSync`.
15
+ * - If data exists (warm cache): sets `providerPctMap` immediately, `isLoading = false`.
16
+ * - If no data (cold cache): leaves `providerPctMap` undefined, `isLoading = false`.
17
+ * 2. Always kicks off an async fetch (fire-and-forget) via `getProviderPctMap` to populate
18
+ * localStorage for next page load. Does NOT update state with the async result.
19
+ *
20
+ * This means:
21
+ * - First page visit (cold): no social proof rendered, no experiment exposure fired.
22
+ * Background fetch populates localStorage for next time.
23
+ * - Second page visit (warm): social proof renders immediately from localStorage.
24
+ * Background refresh keeps localStorage fresh.
25
+ *
26
+ * Callers decide separately (e.g. via Statsig experiment) whether to surface the data.
13
27
  */
14
- const useSocialProof = (traitName, extensionKey, isKillswitchOn = false) => {
28
+ const useSocialProof = (extensionKey, isKillswitchOn = false, baseUriWithNoTrailingSlash = '') => {
15
29
  const isEnabled = isKillswitchOn;
16
- const [providerPctMap, setProviderPctMap] = useState(undefined);
17
- const [isPersonalizationLoading, setIsPersonalizationLoading] = useState(false);
30
+ const [snapshot] = useState(() => {
31
+ if (!isEnabled) {
32
+ return {
33
+ cloudId: undefined,
34
+ providerPctMap: null
35
+ };
36
+ }
37
+ const cloudId = getCurrentSiteCloudIdSync(baseUriWithNoTrailingSlash);
38
+ return {
39
+ cloudId,
40
+ providerPctMap: getProviderPctMapSync(cloudId, SOCIAL_PROOF_TRAIT_NAME)
41
+ };
42
+ });
43
+
44
+ // Fire-and-forget: warm caches for future mounts only. Never update this mount's treatment UI.
18
45
  useEffect(() => {
19
46
  if (!isEnabled) {
20
47
  return;
21
48
  }
22
- let cancelled = false;
23
- setIsPersonalizationLoading(true);
24
- getProviderPctMap(traitName).then(pctMap => {
25
- if (!cancelled) {
26
- setProviderPctMap(pctMap);
27
- setIsPersonalizationLoading(false);
28
- }
49
+ void getCurrentSiteCloudId(baseUriWithNoTrailingSlash).then(cloudId => {
50
+ void getProviderPctMap(cloudId, SOCIAL_PROOF_TRAIT_NAME);
29
51
  });
30
- return () => {
31
- cancelled = true;
32
- };
33
- }, [isEnabled, traitName]);
52
+ }, [baseUriWithNoTrailingSlash, isEnabled]);
34
53
  return useMemo(() => {
35
54
  if (!isEnabled) {
36
55
  return NOT_ENABLED_RESULT;
37
56
  }
38
57
  return {
39
- connectedPct: extensionKey && providerPctMap ? providerPctMap[extensionKey] : undefined,
40
- isEnabled,
41
- isLoading: isPersonalizationLoading
58
+ connectedPct: extensionKey && snapshot.providerPctMap ? snapshot.providerPctMap[extensionKey] : undefined,
59
+ isEnabled: Boolean(snapshot.cloudId && snapshot.providerPctMap),
60
+ isLoading: false // sync read is instant; never in "loading" state
42
61
  };
43
- }, [extensionKey, isEnabled, isPersonalizationLoading, providerPctMap]);
62
+ }, [extensionKey, isEnabled, snapshot]);
44
63
  };
45
64
  export default useSocialProof;
@@ -0,0 +1,41 @@
1
+ import { useMemo } from 'react';
2
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
+ import useSocialProof from '../use-social-proof';
4
+ const TIER_THRESHOLD = 30;
5
+
6
+ /**
7
+ * Returns enrollment and treatment state for the social proof unauth block card experiment.
8
+ *
9
+ * Delegates data-fetching to `useSocialProof` (Sasha's shared service layer),
10
+ * which handles cloudId resolution, TAP trait fetch, localStorage caching,
11
+ * request deduping, and sync reads.
12
+ *
13
+ * This hook adds the experiment/cohort layer on top:
14
+ * - Only fires exposure (via editorExperiment) when data has loaded.
15
+ * - Derives `tier` and `isTreatment` for rendering decisions.
16
+ *
17
+ * Expected to be called only inside the fg-enabled branch (via componentWithFG).
18
+ *
19
+ * @param extensionKey - The extensionKey of the current 3P provider (e.g. 'google-object-provider').
20
+ */
21
+ const useSocialProofExperiment = (extensionKey, baseUriWithNoTrailingSlash = '') => {
22
+ const {
23
+ connectedPct,
24
+ isEnabled,
25
+ isLoading
26
+ } = useSocialProof(extensionKey, true, baseUriWithNoTrailingSlash);
27
+ return useMemo(() => {
28
+ const hasSocialProofData = connectedPct !== undefined;
29
+
30
+ // Only fire exposure when data has loaded (prevents exposure inflation)
31
+ const isTreatment = isEnabled && !isLoading && hasSocialProofData ? editorExperiment('social_proof_3p_unauth_block_exp', true) : false;
32
+ const tier = connectedPct !== undefined && connectedPct >= TIER_THRESHOLD ? 'not-low' : 'low';
33
+ return {
34
+ isTreatment,
35
+ tier,
36
+ connectedPct,
37
+ isLoading
38
+ };
39
+ }, [isEnabled, isLoading, connectedPct]);
40
+ };
41
+ export default useSocialProofExperiment;