@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
@@ -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
+ var 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
+ var useSocialProofExperiment = function useSocialProofExperiment(extensionKey) {
22
+ var baseUriWithNoTrailingSlash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
23
+ var _useSocialProof = useSocialProof(extensionKey, true, baseUriWithNoTrailingSlash),
24
+ connectedPct = _useSocialProof.connectedPct,
25
+ isEnabled = _useSocialProof.isEnabled,
26
+ isLoading = _useSocialProof.isLoading;
27
+ return useMemo(function () {
28
+ var hasSocialProofData = connectedPct !== undefined;
29
+
30
+ // Only fire exposure when data has loaded (prevents exposure inflation)
31
+ var isTreatment = isEnabled && !isLoading && hasSocialProofData ? editorExperiment('social_proof_3p_unauth_block_exp', true) : false;
32
+ var tier = connectedPct !== undefined && connectedPct >= TIER_THRESHOLD ? 'not-low' : 'low';
33
+ return {
34
+ isTreatment: isTreatment,
35
+ tier: tier,
36
+ connectedPct: connectedPct,
37
+ isLoading: isLoading
38
+ };
39
+ }, [isEnabled, isLoading, connectedPct]);
40
+ };
41
+ export default useSocialProofExperiment;
@@ -5,32 +5,38 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
5
5
  import _regeneratorRuntime from "@babel/runtime/regenerator";
6
6
  import { StorageClient } from '@atlaskit/frontend-utilities/storage-client';
7
7
  import { request } from '@atlaskit/linking-common';
8
-
9
- /**
10
- * Logical key shape matches smart-card storage conventions (see personalization-service). {@link StorageClient}
11
- * stores rows as `<clientKey>_<itemKey>` with `clientKey === '@atlaskit/smart-card'` and
12
- * `itemKey === 'site-cloud-id:v1'` (no further scope segments; unlike `pct-map:v1:`, this is a single fixed row).
13
- */
14
- var SMART_CARD_STORAGE_SCOPE = '@atlaskit/smart-card';
15
- export var CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY = 'site-cloud-id:v1';
8
+ var SMART_CARD_STORAGE_SCOPE = 'smart-card-social-proof';
9
+ export var CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY_PREFIX = 'site-cloud-id:v1:';
10
+ export var CURRENT_SITE_CLOUD_ID_TTL_MS = 24 * 60 * 60 * 1000;
16
11
  var smartCardStorage = new StorageClient(SMART_CARD_STORAGE_SCOPE);
12
+ function normalizeBaseUri() {
13
+ var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
14
+ return baseUriWithNoTrailingSlash.replace(/\/$/, '');
15
+ }
16
+ function cloudIdStorageItemKey() {
17
+ var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
18
+ return "".concat(CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY_PREFIX).concat(encodeURIComponent(normalizeBaseUri(baseUriWithNoTrailingSlash)));
19
+ }
17
20
 
18
21
  /** Keys written by this service in localStorage when using {@link smartCardStorage}. */
19
- export var CURRENT_SITE_CLOUD_ID_LOCAL_STORAGE_KEY = "".concat(SMART_CARD_STORAGE_SCOPE, "_").concat(CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY);
22
+ export var getCurrentSiteCloudIdLocalStorageKey = function getCurrentSiteCloudIdLocalStorageKey() {
23
+ var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
24
+ return "".concat(SMART_CARD_STORAGE_SCOPE, "_").concat(cloudIdStorageItemKey(baseUriWithNoTrailingSlash));
25
+ };
26
+
27
+ /** Backwards-compatible default-scope key for existing tests and external assertions. */
28
+ export var CURRENT_SITE_CLOUD_ID_LOCAL_STORAGE_KEY = getCurrentSiteCloudIdLocalStorageKey();
20
29
  export var CurrentSiteCloudIdService = /*#__PURE__*/function () {
21
30
  function CurrentSiteCloudIdService() {
22
31
  _classCallCheck(this, CurrentSiteCloudIdService);
23
- /**
24
- * Holds the shared tenant_info work: one in-flight fetch, then (on success) a settled promise for the session cloud
25
- * id so later callers never trigger another `tenant_info` in the same page lifetime (until {@link clearCache}).
26
- */
27
- _defineProperty(this, "tenantInfoInflightPromise", null);
32
+ _defineProperty(this, "tenantInfoInflightPromises", new Map());
28
33
  }
29
34
  return _createClass(CurrentSiteCloudIdService, [{
30
35
  key: "readStoredCloudId",
31
36
  value: function readStoredCloudId() {
37
+ var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
32
38
  try {
33
- var cloudId = smartCardStorage.getItem(CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY);
39
+ var cloudId = smartCardStorage.getItem(cloudIdStorageItemKey(baseUriWithNoTrailingSlash));
34
40
  if (typeof cloudId !== 'string' || !cloudId || cloudId === 'undefined') {
35
41
  return undefined;
36
42
  }
@@ -41,12 +47,12 @@ export var CurrentSiteCloudIdService = /*#__PURE__*/function () {
41
47
  }
42
48
  }, {
43
49
  key: "writeStoredCloudId",
44
- value: function writeStoredCloudId(cloudId) {
50
+ value: function writeStoredCloudId(baseUriWithNoTrailingSlash, cloudId) {
45
51
  if (!cloudId) {
46
52
  return;
47
53
  }
48
54
  try {
49
- smartCardStorage.setItemWithExpiry(CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY, cloudId);
55
+ smartCardStorage.setItemWithExpiry(cloudIdStorageItemKey(baseUriWithNoTrailingSlash), cloudId, CURRENT_SITE_CLOUD_ID_TTL_MS);
50
56
  } catch (_unused2) {
51
57
  // Quota, private-mode, SSR, etc. — same intent as personalization-service.
52
58
  }
@@ -55,67 +61,62 @@ export var CurrentSiteCloudIdService = /*#__PURE__*/function () {
55
61
  key: "ensureTenantInfoInflightStarted",
56
62
  value: function ensureTenantInfoInflightStarted(baseUriWithNoTrailingSlash) {
57
63
  var _this = this;
58
- if (this.tenantInfoInflightPromise !== null) {
59
- return;
64
+ var baseUri = normalizeBaseUri(baseUriWithNoTrailingSlash);
65
+ var existing = this.tenantInfoInflightPromises.get(baseUri);
66
+ if (existing) {
67
+ return existing;
60
68
  }
61
- this.tenantInfoInflightPromise = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
69
+ var promise = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
62
70
  var response, cloudId;
63
71
  return _regeneratorRuntime.wrap(function _callee$(_context) {
64
72
  while (1) switch (_context.prev = _context.next) {
65
73
  case 0:
66
74
  _context.prev = 0;
67
75
  _context.next = 3;
68
- return request('get', baseUriWithNoTrailingSlash + '/_edge/tenant_info');
76
+ return request('get', "".concat(baseUri, "/_edge/tenant_info"));
69
77
  case 3:
70
78
  response = _context.sent;
71
79
  cloudId = response === null || response === void 0 ? void 0 : response.cloudId;
72
80
  if (cloudId) {
73
- _this.writeStoredCloudId(cloudId);
81
+ _this.writeStoredCloudId(baseUri, cloudId);
74
82
  }
75
- return _context.abrupt("return", cloudId ? cloudId : _this.readStoredCloudId());
83
+ return _context.abrupt("return", cloudId ? cloudId : _this.readStoredCloudId(baseUri));
76
84
  case 9:
77
85
  _context.prev = 9;
78
86
  _context.t0 = _context["catch"](0);
79
- _this.tenantInfoInflightPromise = null;
80
- return _context.abrupt("return", _this.readStoredCloudId());
81
- case 13:
87
+ return _context.abrupt("return", _this.readStoredCloudId(baseUri));
88
+ case 12:
89
+ _context.prev = 12;
90
+ _this.tenantInfoInflightPromises.delete(baseUri);
91
+ return _context.finish(12);
92
+ case 15:
82
93
  case "end":
83
94
  return _context.stop();
84
95
  }
85
- }, _callee, null, [[0, 9]]);
96
+ }, _callee, null, [[0, 9, 12, 15]]);
86
97
  }))();
98
+ this.tenantInfoInflightPromises.set(baseUri, promise);
99
+ return promise;
87
100
  }
88
101
 
89
- /**
90
- * Returns the currently cached cloud id synchronously and starts a background refresh.
91
- * The refresh result is persisted for future calls but is not awaited by this call.
92
- */
102
+ /** Pure synchronous read scoped to the given base URI. */
93
103
  }, {
94
- key: "getCachedCloudIdAndRefresh",
95
- value: function getCachedCloudIdAndRefresh() {
96
- this.ensureTenantInfoInflightStarted('');
97
- return this.readStoredCloudId();
104
+ key: "getStoredCloudId",
105
+ value: function getStoredCloudId() {
106
+ var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
107
+ return this.readStoredCloudId(baseUriWithNoTrailingSlash);
98
108
  }
99
109
 
100
110
  /** Writes tenant cloud id for tests or callers that intentionally warm storage before edge resolves. */
101
111
  }, {
102
112
  key: "persistStoredCloudId",
103
113
  value: function persistStoredCloudId(cloudId) {
104
- this.writeStoredCloudId(cloudId);
114
+ var baseUriWithNoTrailingSlash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
115
+ this.writeStoredCloudId(baseUriWithNoTrailingSlash, cloudId);
105
116
  }
106
-
107
- /**
108
- * When local storage already has a tenant cloud id, it is returned immediately; a background tenant_info refresh
109
- * is still kicked off unless one is already in flight.
110
- *
111
- * Without storage, this awaits the deduped in-flight tenant_info (first concurrent caller chooses the URL;
112
- * all share one promise regardless of subsequent `baseUriWithNoTrailingSlash`).
113
- *
114
- * On network success with no cloud id, or on failure: falls back via {@link readStoredCloudId}.
115
- */
116
117
  }, {
117
118
  key: "get",
118
- value: (function () {
119
+ value: function () {
119
120
  var _get = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
120
121
  var baseUriWithNoTrailingSlash,
121
122
  fromStorage,
@@ -124,16 +125,15 @@ export var CurrentSiteCloudIdService = /*#__PURE__*/function () {
124
125
  while (1) switch (_context2.prev = _context2.next) {
125
126
  case 0:
126
127
  baseUriWithNoTrailingSlash = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : '';
127
- fromStorage = this.readStoredCloudId();
128
- this.ensureTenantInfoInflightStarted(baseUriWithNoTrailingSlash);
128
+ fromStorage = this.readStoredCloudId(baseUriWithNoTrailingSlash);
129
129
  if (!fromStorage) {
130
- _context2.next = 5;
130
+ _context2.next = 4;
131
131
  break;
132
132
  }
133
133
  return _context2.abrupt("return", fromStorage);
134
+ case 4:
135
+ return _context2.abrupt("return", this.ensureTenantInfoInflightStarted(baseUriWithNoTrailingSlash));
134
136
  case 5:
135
- return _context2.abrupt("return", this.tenantInfoInflightPromise);
136
- case 6:
137
137
  case "end":
138
138
  return _context2.stop();
139
139
  }
@@ -143,33 +143,20 @@ export var CurrentSiteCloudIdService = /*#__PURE__*/function () {
143
143
  return _get.apply(this, arguments);
144
144
  }
145
145
  return get;
146
- }() /** Clears session pin and persisted storage so the next {@link get} is a fresh tenant_info fetch. */)
146
+ }() /** Clears session pin and persisted storage so the next {@link get} is a fresh tenant_info fetch. */
147
147
  }, {
148
148
  key: "clearCache",
149
149
  value: function clearCache() {
150
- this.tenantInfoInflightPromise = null;
151
- smartCardStorage.removeItem(CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY);
150
+ this.tenantInfoInflightPromises.clear();
152
151
  }
153
152
  }]);
154
153
  }();
155
154
  export var currentSiteCloudIdService = new CurrentSiteCloudIdService();
156
-
157
- /**
158
- * Resolves the current site cloud id through the module-level {@link currentSiteCloudIdService}.
159
- * Returns a stored cloud id immediately when one exists; otherwise waits for the shared
160
- * `tenant_info` request and persists the result for subsequent cached reads.
161
- */
155
+ export function getCurrentSiteCloudIdSync() {
156
+ var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
157
+ return currentSiteCloudIdService.getStoredCloudId(baseUriWithNoTrailingSlash);
158
+ }
162
159
  export var getCurrentSiteCloudId = function getCurrentSiteCloudId() {
163
160
  var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
164
161
  return currentSiteCloudIdService.get(baseUriWithNoTrailingSlash);
165
- };
166
-
167
- /**
168
- * Reads the current site cloud id from browser storage (the `site-cloud-id:v1` row) via the
169
- * module-level {@link currentSiteCloudIdService} singleton, without awaiting network work.
170
- * Calling this also starts the shared `tenant_info` refresh in the background when one is not
171
- * already running, so a later call can observe a refreshed value when available.
172
- */
173
- export function getCachedCurrentSiteCloudIdAndRefresh() {
174
- return currentSiteCloudIdService.getCachedCloudIdAndRefresh();
175
- }
162
+ };
@@ -6,21 +6,19 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
6
6
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
7
7
  import _regeneratorRuntime from "@babel/runtime/regenerator";
8
8
  import { StorageClient } from '@atlaskit/frontend-utilities/storage-client';
9
- import { getCurrentSiteCloudId, getCachedCurrentSiteCloudIdAndRefresh } from '../current-site-cloud-id';
9
+ import { getCurrentSiteCloudId, getCurrentSiteCloudIdSync } from '../current-site-cloud-id';
10
10
  var BASE_URL = '/gateway/api/tap-delivery/api/v3/personalization';
11
-
12
- /**
13
- * Logical key shape: `@atlaskit/smart-card:<feature>:<schema-version>:<scope>` (see smart-card
14
- * storage conventions). {@link StorageClient} narrows the localStorage key to
15
- * `<clientKey>_<itemKey>` with `clientKey === '@atlaskit/smart-card'` and
16
- * `itemKey === 'pct-map:v1:<cloudId>:<traitName>'` (scope segments URI-encoded).
17
- */
18
- export var PERSONALIZATION_STORAGE_SCOPE = '@atlaskit/smart-card';
11
+ export var PERSONALIZATION_STORAGE_SCOPE = 'smart-card-social-proof';
19
12
  export var PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = 'pct-map:v1:';
13
+ export var PERSONALIZATION_PROVIDER_PCT_TTL_MS = 24 * 60 * 60 * 1000;
14
+ export var SOCIAL_PROOF_TRAIT_NAME = 'sl_3p_connected_providers_site_pct';
20
15
  var smartCardStorage = new StorageClient(PERSONALIZATION_STORAGE_SCOPE);
21
16
 
22
17
  /** Keys written by this service in localStorage when using {@link smartCardStorage}. */
23
18
  var LOCAL_STORAGE_ROW_KEY_PREFIX = "".concat(PERSONALIZATION_STORAGE_SCOPE, "_").concat(PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX);
19
+ function scopedCacheKey(cloudId, traitName) {
20
+ return "".concat(cloudId, ":").concat(traitName);
21
+ }
24
22
  function pctMapStorageItemKey(cloudId, traitName) {
25
23
  return "".concat(PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX).concat(encodeURIComponent(cloudId), ":").concat(encodeURIComponent(traitName));
26
24
  }
@@ -72,53 +70,46 @@ export var PersonalizationService = /*#__PURE__*/function () {
72
70
  _defineProperty(this, "cache", new Map());
73
71
  }
74
72
  return _createClass(PersonalizationService, [{
75
- key: "getCachedProviderPctMapAndRefresh",
76
- value:
77
- /**
78
- * Returns the currently cached provider percentage map synchronously and starts a background refresh.
79
- * The refresh result is persisted for future calls but is not awaited by this call.
80
- */
81
- function getCachedProviderPctMapAndRefresh(traitName) {
82
- var cloudId = getCachedCurrentSiteCloudIdAndRefresh();
83
- var fromStorage = cloudId && this.readStoredProviderPctMap(cloudId, traitName) || null;
84
- void this.getProviderPctMap(traitName);
85
- return fromStorage;
73
+ key: "getProviderPctMapSync",
74
+ value: /** Pure synchronous read for an explicit cloud id / trait pair. */
75
+ function getProviderPctMapSync(cloudId, traitName) {
76
+ if (!cloudId) {
77
+ return null;
78
+ }
79
+ return this.readStoredProviderPctMap(cloudId, traitName);
86
80
  }
87
81
  }, {
88
82
  key: "getProviderPctMap",
89
83
  value: function () {
90
- var _getProviderPctMap = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(traitName) {
84
+ var _getProviderPctMap = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(cloudId, traitName) {
91
85
  var _this = this;
92
- var cachedPromise, promise;
86
+ var cacheKey, cachedPromise, promise, retryablePromise;
93
87
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
94
88
  while (1) switch (_context2.prev = _context2.next) {
95
89
  case 0:
96
- cachedPromise = this.cache.get(traitName);
90
+ if (cloudId) {
91
+ _context2.next = 2;
92
+ break;
93
+ }
94
+ return _context2.abrupt("return", undefined);
95
+ case 2:
96
+ cacheKey = scopedCacheKey(cloudId, traitName);
97
+ cachedPromise = this.cache.get(cacheKey);
97
98
  if (!cachedPromise) {
98
- _context2.next = 3;
99
+ _context2.next = 6;
99
100
  break;
100
101
  }
101
102
  return _context2.abrupt("return", cachedPromise);
102
- case 3:
103
+ case 6:
103
104
  promise = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
104
- var cloudId, traits, trait, mapped;
105
+ var traits, trait, mapped;
105
106
  return _regeneratorRuntime.wrap(function _callee$(_context) {
106
107
  while (1) switch (_context.prev = _context.next) {
107
108
  case 0:
108
109
  _context.prev = 0;
109
110
  _context.next = 3;
110
- return getCurrentSiteCloudId();
111
- case 3:
112
- cloudId = _context.sent;
113
- if (cloudId) {
114
- _context.next = 6;
115
- break;
116
- }
117
- return _context.abrupt("return", undefined);
118
- case 6:
119
- _context.next = 8;
120
111
  return fetchSiteTraits(cloudId);
121
- case 8:
112
+ case 3:
122
113
  traits = _context.sent;
123
114
  trait = traits.find(function (t) {
124
115
  return t.name === traitName;
@@ -128,25 +119,28 @@ export var PersonalizationService = /*#__PURE__*/function () {
128
119
  _this.writeStoredProviderPctMap(cloudId, traitName, mapped);
129
120
  }
130
121
  return _context.abrupt("return", mapped);
131
- case 15:
132
- _context.prev = 15;
122
+ case 10:
123
+ _context.prev = 10;
133
124
  _context.t0 = _context["catch"](0);
134
125
  return _context.abrupt("return", undefined);
135
- case 18:
126
+ case 13:
136
127
  case "end":
137
128
  return _context.stop();
138
129
  }
139
- }, _callee, null, [[0, 15]]);
130
+ }, _callee, null, [[0, 10]]);
140
131
  }))();
141
- this.cache.set(traitName, promise);
142
- return _context2.abrupt("return", promise);
143
- case 6:
132
+ retryablePromise = promise.finally(function () {
133
+ _this.cache.delete(cacheKey);
134
+ });
135
+ this.cache.set(cacheKey, retryablePromise);
136
+ return _context2.abrupt("return", retryablePromise);
137
+ case 10:
144
138
  case "end":
145
139
  return _context2.stop();
146
140
  }
147
141
  }, _callee2, this);
148
142
  }));
149
- function getProviderPctMap(_x2) {
143
+ function getProviderPctMap(_x2, _x3) {
150
144
  return _getProviderPctMap.apply(this, arguments);
151
145
  }
152
146
  return getProviderPctMap;
@@ -168,7 +162,7 @@ export var PersonalizationService = /*#__PURE__*/function () {
168
162
  key: "writeStoredProviderPctMap",
169
163
  value: function writeStoredProviderPctMap(cloudId, traitName, map) {
170
164
  try {
171
- smartCardStorage.setItemWithExpiry(pctMapStorageItemKey(cloudId, traitName), map);
165
+ smartCardStorage.setItemWithExpiry(pctMapStorageItemKey(cloudId, traitName), map, PERSONALIZATION_PROVIDER_PCT_TTL_MS);
172
166
  } catch (_unused3) {
173
167
  // Quota, private-mode, etc.
174
168
  }
@@ -237,22 +231,25 @@ export var PersonalizationService = /*#__PURE__*/function () {
237
231
  }]);
238
232
  }();
239
233
  export var personalizationService = new PersonalizationService();
240
-
241
- /**
242
- * Resolves the provider percentage map for a TAP Delivery trait through the module-level
243
- * {@link personalizationService}. Work is deduped per trait name for the page lifetime, and a
244
- * successful response is persisted by cloud id and trait name for later cached reads.
245
- */
246
- export var getProviderPctMap = function getProviderPctMap(traitName) {
247
- return personalizationService.getProviderPctMap(traitName);
234
+ export var getProviderPctMap = function getProviderPctMap(cloudId, traitName) {
235
+ return personalizationService.getProviderPctMap(cloudId, traitName);
248
236
  };
237
+ export function getProviderPctMapSync(cloudId, traitName) {
238
+ return personalizationService.getProviderPctMapSync(cloudId, traitName);
239
+ }
249
240
 
250
241
  /**
251
- * Reads the provider percentage map for a trait from browser storage via the module-level
252
- * {@link personalizationService} singleton, without awaiting network work.
253
- * Calling this also starts the trait-scoped shared refresh in the background, so a later call can
254
- * use a refreshed value when it becomes available.
242
+ * Backwards-compatible cache-first helper for inline-card social proof callers.
243
+ *
244
+ * Reads the persisted provider percentage map synchronously using the current site cloud id, then
245
+ * starts a background refresh for subsequent mounts. The async result intentionally does not affect
246
+ * the current call site, matching the warm-cache-only rendering contract.
255
247
  */
256
248
  export function getCachedProviderPctMapAndRefresh(traitName) {
257
- return personalizationService.getCachedProviderPctMapAndRefresh(traitName);
249
+ var cloudId = getCurrentSiteCloudIdSync();
250
+ var providerPctMap = getProviderPctMapSync(cloudId, traitName);
251
+ void getCurrentSiteCloudId().then(function (resolvedCloudId) {
252
+ void getProviderPctMap(resolvedCloudId, traitName);
253
+ });
254
+ return providerPctMap;
258
255
  }
@@ -4,7 +4,7 @@ export var ANALYTICS_CHANNEL = 'media';
4
4
  export var context = {
5
5
  componentName: 'smart-cards',
6
6
  packageName: "@atlaskit/smart-card" || '',
7
- packageVersion: "44.6.1" || ''
7
+ packageVersion: "44.7.0" || ''
8
8
  };
9
9
  export var TrackQuickActionType = /*#__PURE__*/function (TrackQuickActionType) {
10
10
  TrackQuickActionType["StatusUpdate"] = "StatusUpdate";
@@ -0,0 +1,29 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React from 'react';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import { Box, Text } from '@atlaskit/primitives/compiled';
5
+ import { messages } from '../../../messages';
6
+ // TODO: remove when social-proof-3p-unauth-block-fg is cleaned up
7
+ var SocialProofMessage = function SocialProofMessage(_ref) {
8
+ var tier = _ref.tier,
9
+ connectedPct = _ref.connectedPct,
10
+ providerName = _ref.providerName,
11
+ _ref$testId = _ref.testId,
12
+ testId = _ref$testId === void 0 ? 'smart-block-social-proof-message' : _ref$testId;
13
+ var message = tier === 'not-low' ? messages.pre_auth_block_social_proof_not_low : messages.pre_auth_block_social_proof_low;
14
+ return /*#__PURE__*/React.createElement(Box, {
15
+ testId: testId
16
+ }, /*#__PURE__*/React.createElement(FormattedMessage, _extends({}, message, {
17
+ values: {
18
+ percentage: connectedPct !== null && connectedPct !== void 0 ? connectedPct : 0,
19
+ provider: providerName,
20
+ b: function b(chunks) {
21
+ return /*#__PURE__*/React.createElement(Text, {
22
+ as: "strong",
23
+ weight: "bold"
24
+ }, chunks);
25
+ }
26
+ }
27
+ })));
28
+ };
29
+ export default SocialProofMessage;
@@ -1,19 +1,26 @@
1
1
  /* UnauthorisedView.tsx generated by @compiled/babel-plugin v0.39.1 */
2
2
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
3
  import _extends from "@babel/runtime/helpers/extends";
4
- var _excluded = ["testId"];
4
+ var _excluded = ["content", "providerName", "testId"],
5
+ _excluded2 = ["testId"],
6
+ _excluded3 = ["testId"];
5
7
  import "./UnauthorisedView.compiled.css";
6
8
  import * as React from 'react';
7
9
  import { ax, ix } from "@compiled/react/runtime";
8
10
  import { useCallback, useMemo } from 'react';
9
11
  import { FormattedMessage } from 'react-intl';
12
+ import { di } from 'react-magnetic-di';
13
+ import { AnalyticsContext } from '@atlaskit/analytics-next';
10
14
  import { extractSmartLinkProvider } from '@atlaskit/link-extractors';
15
+ import { useSmartLinkContext } from '@atlaskit/link-provider';
16
+ import { componentWithFG } from '@atlaskit/platform-feature-flags-react';
11
17
  import { Box } from '@atlaskit/primitives/compiled';
12
18
  import { useAnalyticsEvents } from '../../../common/analytics/generated/use-analytics-events';
13
19
  import { ElementName, SmartLinkDirection, SmartLinkSize, SmartLinkWidth } from '../../../constants';
14
20
  import { messages } from '../../../messages';
15
21
  import { useFlexibleCardContext } from '../../../state/flexible-ui-context';
16
22
  import { hasAuthScopeOverrides } from '../../../state/helpers';
23
+ import useSocialProofExperiment from '../../../state/hooks/use-social-proof-experiment';
17
24
  import UnauthorisedViewContent from '../../common/UnauthorisedViewContent';
18
25
  import FlexibleCard from '../../FlexibleCard';
19
26
  import ActionGroup from '../../FlexibleCard/components/blocks/action-group';
@@ -23,6 +30,7 @@ import { renderElementItems } from '../../FlexibleCard/components/blocks/utils';
23
30
  import { LinkIcon, Title } from '../../FlexibleCard/components/elements';
24
31
  import { AuthorizeAction } from '../actions/AuthorizeAction';
25
32
  import unauthIllustrationGeneral from './assets/general@2x.png';
33
+ import SocialProofMessage from './SocialProofMessage';
26
34
  import { FlexibleCardUiOptions, titleBlockOptions } from './utils';
27
35
  import { withFlexibleUIBlockCardStyle } from './utils/withFlexibleUIBlockCardStyle';
28
36
  var contentStyles = null;
@@ -161,15 +169,12 @@ var UnauthorisedBlock = function UnauthorisedBlock(_ref) {
161
169
  * @see SmartLinkStatus
162
170
  * @see FlexibleCardProps
163
171
  */
164
- var UnauthorisedView = function UnauthorisedView(_ref3) {
165
- var _extractSmartLinkProv;
166
- var _ref3$testId = _ref3.testId,
167
- testId = _ref3$testId === void 0 ? 'smart-block-unauthorized-view' : _ref3$testId,
172
+ var UnauthorisedViewFrame = function UnauthorisedViewFrame(_ref3) {
173
+ var content = _ref3.content,
174
+ providerName = _ref3.providerName,
175
+ testId = _ref3.testId,
168
176
  props = _objectWithoutProperties(_ref3, _excluded);
169
- var cardState = props.cardState,
170
- onAuthorize = props.onAuthorize;
171
- var providerName = (_extractSmartLinkProv = extractSmartLinkProvider(cardState.details)) === null || _extractSmartLinkProv === void 0 ? void 0 : _extractSmartLinkProv.text;
172
- var isProductIntegrationSupported = hasAuthScopeOverrides(cardState.details);
177
+ var onAuthorize = props.onAuthorize;
173
178
  var _useAnalyticsEvents = useAnalyticsEvents(),
174
179
  fireEvent = _useAnalyticsEvents.fireEvent;
175
180
  var handleAuthorize = useCallback(function () {
@@ -178,17 +183,6 @@ var UnauthorisedView = function UnauthorisedView(_ref3) {
178
183
  onAuthorize();
179
184
  }
180
185
  }, [onAuthorize, fireEvent]);
181
- var content = useMemo(function () {
182
- return onAuthorize ? /*#__PURE__*/React.createElement(UnauthorisedViewContent, {
183
- providerName: providerName,
184
- isProductIntegrationSupported: isProductIntegrationSupported,
185
- testId: testId
186
- }) : /*#__PURE__*/React.createElement(FormattedMessage, _extends({}, messages[providerName ? 'unauthorised_account_description' : 'unauthorised_account_description_no_provider'], {
187
- values: {
188
- context: providerName
189
- }
190
- }));
191
- }, [isProductIntegrationSupported, onAuthorize, providerName, testId]);
192
186
  var actions = useMemo(function () {
193
187
  return onAuthorize ? [AuthorizeAction(handleAuthorize, providerName)] : [];
194
188
  }, [handleAuthorize, onAuthorize, providerName]);
@@ -209,5 +203,76 @@ var UnauthorisedView = function UnauthorisedView(_ref3) {
209
203
  className: ax(["_11c8fhey _syazi7uo _19pku2gc", actions.length > 0 && "_1reo15vq _18m915vq _otyrpxbi _p12f1osq _o5721q9c _1bto1l2s"])
210
204
  }, content)));
211
205
  };
206
+ var UnauthorisedViewBase = function UnauthorisedViewBase(_ref4) {
207
+ var _extractSmartLinkProv;
208
+ var _ref4$testId = _ref4.testId,
209
+ testId = _ref4$testId === void 0 ? 'smart-block-unauthorized-view' : _ref4$testId,
210
+ props = _objectWithoutProperties(_ref4, _excluded2);
211
+ var cardState = props.cardState,
212
+ onAuthorize = props.onAuthorize;
213
+ var providerName = (_extractSmartLinkProv = extractSmartLinkProvider(cardState.details)) === null || _extractSmartLinkProv === void 0 ? void 0 : _extractSmartLinkProv.text;
214
+ var isProductIntegrationSupported = hasAuthScopeOverrides(cardState.details);
215
+ var content = useMemo(function () {
216
+ return onAuthorize ? /*#__PURE__*/React.createElement(UnauthorisedViewContent, {
217
+ providerName: providerName,
218
+ isProductIntegrationSupported: isProductIntegrationSupported,
219
+ testId: testId
220
+ }) : /*#__PURE__*/React.createElement(FormattedMessage, _extends({}, messages[providerName ? 'unauthorised_account_description' : 'unauthorised_account_description_no_provider'], {
221
+ values: {
222
+ context: providerName
223
+ }
224
+ }));
225
+ }, [isProductIntegrationSupported, onAuthorize, providerName, testId]);
226
+ return /*#__PURE__*/React.createElement(UnauthorisedViewFrame, _extends({}, props, {
227
+ content: content,
228
+ providerName: providerName,
229
+ testId: testId
230
+ }));
231
+ };
232
+
233
+ /**
234
+ * Experiment wrapper: fires social proof exposure and renders social proof UI when FG is on.
235
+ * TODO: remove when social-proof-3p-unauth-block-fg is cleaned up
236
+ */
237
+ var UnauthorisedViewWithExperiment = function UnauthorisedViewWithExperiment(_ref5) {
238
+ var _props$cardState, _extractSmartLinkProv2;
239
+ var _ref5$testId = _ref5.testId,
240
+ testId = _ref5$testId === void 0 ? 'smart-block-unauthorized-view' : _ref5$testId,
241
+ props = _objectWithoutProperties(_ref5, _excluded3);
242
+ 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;
243
+ var providerName = (_extractSmartLinkProv2 = extractSmartLinkProvider(props.cardState.details)) === null || _extractSmartLinkProv2 === void 0 ? void 0 : _extractSmartLinkProv2.text;
244
+ var _useSmartLinkContext = useSmartLinkContext(),
245
+ connections = _useSmartLinkContext.connections;
246
+ var _useSocialProofExperi = useSocialProofExperiment(providerName ? extensionKey : undefined, connections.client.baseUrlOverride),
247
+ isTreatment = _useSocialProofExperi.isTreatment,
248
+ tier = _useSocialProofExperi.tier,
249
+ connectedPct = _useSocialProofExperi.connectedPct;
250
+ if (!isTreatment || !providerName) {
251
+ return /*#__PURE__*/React.createElement(UnauthorisedViewBase, _extends({}, props, {
252
+ testId: testId
253
+ }));
254
+ }
255
+ return /*#__PURE__*/React.createElement(AnalyticsContext, {
256
+ data: {
257
+ attributes: {
258
+ experiment: 'social_proof_3p_unauth_block_exp',
259
+ cohort: 'treatment',
260
+ tier: tier
261
+ }
262
+ }
263
+ }, /*#__PURE__*/React.createElement(UnauthorisedViewFrame, _extends({}, props, {
264
+ content: /*#__PURE__*/React.createElement(SocialProofMessage, {
265
+ tier: tier,
266
+ connectedPct: connectedPct,
267
+ providerName: providerName
268
+ }),
269
+ providerName: providerName,
270
+ testId: testId
271
+ })));
272
+ };
273
+ var UnauthorisedViewWithoutExperiment = function UnauthorisedViewWithoutExperiment(props) {
274
+ return /*#__PURE__*/React.createElement(UnauthorisedViewBase, props);
275
+ };
276
+ var UnauthorisedView = componentWithFG('social-proof-3p-unauth-block-fg', UnauthorisedViewWithExperiment, UnauthorisedViewWithoutExperiment);
212
277
  var _default_1 = withFlexibleUIBlockCardStyle(UnauthorisedView);
213
278
  export default _default_1;
@@ -15,7 +15,7 @@ import LinkWarningModal from './LinkWarningModal';
15
15
  import { useLinkWarningModal } from './LinkWarningModal/hooks/use-link-warning-modal';
16
16
  var PACKAGE_DATA = {
17
17
  packageName: "@atlaskit/smart-card",
18
- packageVersion: "44.6.1",
18
+ packageVersion: "44.7.0",
19
19
  componentName: 'linkUrl'
20
20
  };
21
21
  var Anchor = withLinkClickedEvent('a');