@atlaskit/smart-card 44.7.0 → 44.7.2

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 +18 -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 +10 -4
  59. package/smart-card.docs.tsx +2 -2
@@ -1,7 +1,7 @@
1
1
  import { type MessageDescriptor } from 'react-intl';
2
2
  export type RequestAccessMessageKey = 'click_to_join' | 'click_to_join_description' | 'forbidden_description' | 'request_access' | 'request_access_description' | 'request_access_pending' | 'request_access_pending_title' | 'request_access_pending_description' | 'request_denied_description' | 'default_no_access_title' | 'direct_access_title' | 'direct_access_description' | 'direct_access' | 'access_exists_description' | 'not_found_description' | 'not_found_title';
3
3
  export type RovoChatActionMessageKey = 'rovo_prompt_context_generic' | 'rovo_prompt_context_generic_plural' | 'rovo_prompt_context_confluence_page' | 'rovo_prompt_context_confluence_page_short' | 'rovo_prompt_context_jira_work_item' | 'rovo_prompt_context_jira_work_item_short' | 'rovo_prompt_button_recommend_other_sources' | 'rovo_prompt_message_recommend_other_sources' | 'rovo_prompt_button_show_other_mentions' | 'rovo_prompt_message_show_other_mentions' | 'rovo_prompt_button_suggest_improvement' | 'rovo_prompt_message_suggest_improvement' | 'rovo_prompt_message_summarize' | 'rovo_prompt_button_ask_rovo_anything' | 'rovo_prompt_message_ask_rovo_anything' | 'rovo_prompt_button_highlight_relevant_content' | 'rovo_prompt_message_highlight_relevant_content' | 'rovo_prompt_button_identify_key_trends' | 'rovo_prompt_message_identify_key_trends' | 'rovo_prompt_button_identify_key_points' | 'rovo_prompt_message_identify_key_points' | 'rovo_prompt_button_find_open_questions' | 'rovo_prompt_message_find_open_questions' | 'rovo_prompt_button_key_highlights' | 'rovo_prompt_message_key_highlights';
4
- export type MessageKey = 'assigned_to' | 'ai_summarize' | 'change_status' | 'ai_summarized' | 'ai_summarized_abbreviation' | 'ai_summarized_info' | 'ai_summarized_info_short' | 'ai_summarizing' | 'ai_summary_error_generic' | 'ai_summary_error_acceptable_use_violation' | 'ai_summary_error_hipaa_content_detected' | 'ai_summary_error_exceeding_context_length_error' | 'ai_summary_action' | 'ai_summary_action_description' | 'automation_action_title' | 'automation_action_tooltip' | 'automation_action_icon_label' | 'automation_action_confluence_page_modal_title' | 'automation_action_confluence_page_modal_description' | 'copy_summary_action' | 'copy_summary_action_description' | 'copied_summary_action_description' | 'beta' | 'cannot_find_link' | 'compass_applied_components_count' | 'connect_link_account_card' | 'connect_link_account_card_name' | 'connect_link_account_card_description' | 'connect_unauthorised_account_action' | 'connect_inline_social_proof' | 'social_proof_inline_cta_tag_high_with_context' | 'social_proof_inline_cta_tag_high_no_context' | 'social_proof_inline_cta_tag_low_with_context' | 'connect_unauthorised_account_description' | 'connect_unauthorised_account_description_no_provider' | 'continue' | 'copy_url_to_clipboard' | 'copied_url_to_clipboard' | 'could_not_load_link' | 'download' | 'download_description' | 'download_file' | 'follow' | 'follow_project_description' | 'follow_project_descriptionGalaxia' | 'follow_project' | 'follow_goal' | 'follow_goal_description' | 'follow_project_error' | 'follow_project_errorGalaxia' | 'follow_goal_error' | 'go_back' | 'invalid_permissions' | 'invalid_permissions_description' | 'join_to_view' | 'connect_link_account' | 'created_by' | 'created_on_relative' | 'created_on_absolute' | 'check_this_link' | 'delete' | 'edit' | 'learn_more_about_smart_links' | 'learn_more_about_connecting_account' | 'loading' | 'link_safety_warning_message' | 'modified_by' | 'modified_on_relative' | 'modified_on_absolute' | 'more_actions' | 'not_found_title' | 'not_found_description' | 'open_issue_in_jira' | 'open_link_in_a_new_tab' | 'owned_by' | 'owned_by_override' | 'preview_description' | 'preview_improved' | 'preview_modal' | 'preview_panel' | 'preview_close' | 'preview_max_size' | 'preview_min_size' | 'priority_blocker' | 'priority_critical' | 'priority_high' | 'priority_highest' | 'priority_low' | 'priority_lowest' | 'priority_major' | 'priority_medium' | 'priority_minor' | 'priority_trivial' | 'priority_undefined' | 'forbidden_access' | 'pending_request' | 'read_time' | 'restricted_link' | 'request_access_to_view' | 'request_denied' | 'sent_on_relative' | 'sent_on_absolute' | 'status_change_load_error' | 'status_change_permission_error' | 'status_change_update_error' | 'try_again' | 'try_another_account' | 'unauthorised_account_description' | 'unauthorised_account_description_no_provider' | 'unauthorised_account_name' | 'unauthorised_account_name_no_provider' | 'rovo_actions_explore' | 'unassigned' | 'unfollow' | 'unfollow_project_description' | 'unfollow_project_descriptionGalaxia' | 'unfollow_project' | 'unfollow_project_error' | 'unfollow_project_errorGalaxia' | 'unfollow_goal' | 'unfollow_goal_description' | 'unfollow_goal_error' | 'user_attributes' | 'view' | 'viewIn' | 'viewOriginal' | 'actions' | 'add_account' | 'cancel' | 'close' | 'connect_to' | 'connect_account_description' | 'retry' | 'save' | 'unlink_account' | RequestAccessMessageKey | 'related' | 'generic_error_message' | 'related_links_modal_error_title' | 'related_links_modal_error_description' | 'related_links_modal_unavailable_title' | 'related_links_modal_unavailable_description' | 'related_links_modal_title' | 'related_links_view_related_urls' | 'related_links_view_related_links' | 'related_links_found_in' | 'related_links_includes_links_to' | 'related_links_not_found' | 'join_to_viewIssueTermRefresh' | 'open_issue_in_jiraIssueTermRefresh' | 'request_access_to_viewIssueTermRefresh' | 'team_members_count' | 'status_change_permission_errorIssueTermRefresh' | 'connect_unauthorised_account_description_appify' | 'connect_unauthorised_account_description_no_provider_appify' | 'learn_more_about_connecting_account_experiment_shorter' | 'learn_more_about_connecting_account_appify' | 'rovo_summary_loading' | 'ai_disclaimer' | 'rovo_unauthorised_title' | 'rovo_unauthorised_title_no_provider' | 'rovo_unauthorised_feature_clear_link_names' | 'rovo_unauthorised_feature_understand_linked_docs' | 'rovo_unauthorised_feature_go_deeper_smart_suggestions' | 'rovo_unauthorised_connect_account' | 'rovo_unauthorised_not_now' | 'rovo_chat_action_section_header' | 'rovo_prompt_button_summarize_this' | 'rovo_prompt_button_ask_a_specific_question' | 'rovo_prompt_button_show_me_whats_relevant' | RovoChatActionMessageKey;
4
+ export type MessageKey = 'assigned_to' | 'ai_summarize' | 'change_status' | 'ai_summarized' | 'ai_summarized_abbreviation' | 'ai_summarized_info' | 'ai_summarized_info_short' | 'ai_summarizing' | 'ai_summary_error_generic' | 'ai_summary_error_acceptable_use_violation' | 'ai_summary_error_hipaa_content_detected' | 'ai_summary_error_exceeding_context_length_error' | 'ai_summary_action' | 'ai_summary_action_description' | 'automation_action_title' | 'automation_action_tooltip' | 'automation_action_icon_label' | 'automation_action_confluence_page_modal_title' | 'automation_action_confluence_page_modal_description' | 'copy_summary_action' | 'copy_summary_action_description' | 'copied_summary_action_description' | 'beta' | 'cannot_find_link' | 'compass_applied_components_count' | 'connect_link_account_card' | 'connect_link_account_card_name' | 'connect_link_account_card_description' | 'connect_unauthorised_account_action' | 'connect_inline_social_proof' | 'social_proof_inline_cta_tag_high_with_context' | 'social_proof_inline_cta_tag_high_no_context' | 'social_proof_inline_cta_tag_low_with_context' | 'connect_unauthorised_account_description' | 'connect_unauthorised_account_description_no_provider' | 'continue' | 'copy_url_to_clipboard' | 'copied_url_to_clipboard' | 'could_not_load_link' | 'download' | 'download_description' | 'download_file' | 'follow' | 'follow_project_description' | 'follow_project_descriptionGalaxia' | 'follow_project' | 'follow_goal' | 'follow_goal_description' | 'follow_project_error' | 'follow_project_errorGalaxia' | 'follow_goal_error' | 'go_back' | 'invalid_permissions' | 'invalid_permissions_description' | 'join_to_view' | 'connect_link_account' | 'created_by' | 'created_on_relative' | 'created_on_absolute' | 'check_this_link' | 'delete' | 'edit' | 'learn_more_about_smart_links' | 'learn_more_about_connecting_account' | 'loading' | 'link_safety_warning_message' | 'modified_by' | 'modified_on_relative' | 'modified_on_absolute' | 'more_actions' | 'not_found_title' | 'not_found_description' | 'open_issue_in_jira' | 'open_link_in_a_new_tab' | 'owned_by' | 'owned_by_override' | 'preview_description' | 'preview_improved' | 'preview_modal' | 'preview_panel' | 'preview_close' | 'preview_max_size' | 'preview_min_size' | 'priority_blocker' | 'priority_critical' | 'priority_high' | 'priority_highest' | 'priority_low' | 'priority_lowest' | 'priority_major' | 'priority_medium' | 'priority_minor' | 'priority_trivial' | 'priority_undefined' | 'forbidden_access' | 'pending_request' | 'read_time' | 'restricted_link' | 'request_access_to_view' | 'request_denied' | 'sent_on_relative' | 'sent_on_absolute' | 'status_change_load_error' | 'status_change_permission_error' | 'status_change_update_error' | 'try_again' | 'try_another_account' | 'unauthorised_account_description' | 'unauthorised_account_description_no_provider' | 'unauthorised_account_name' | 'unauthorised_account_name_no_provider' | 'rovo_actions_explore' | 'unassigned' | 'unfollow' | 'unfollow_project_description' | 'unfollow_project_descriptionGalaxia' | 'unfollow_project' | 'unfollow_project_error' | 'unfollow_project_errorGalaxia' | 'unfollow_goal' | 'unfollow_goal_description' | 'unfollow_goal_error' | 'user_attributes' | 'view' | 'viewIn' | 'viewOriginal' | 'actions' | 'add_account' | 'cancel' | 'close' | 'connect_to' | 'connect_account_description' | 'retry' | 'save' | 'unlink_account' | RequestAccessMessageKey | 'related' | 'generic_error_message' | 'related_links_modal_error_title' | 'related_links_modal_error_description' | 'related_links_modal_unavailable_title' | 'related_links_modal_unavailable_description' | 'related_links_modal_title' | 'related_links_view_related_urls' | 'related_links_view_related_links' | 'related_links_found_in' | 'related_links_includes_links_to' | 'related_links_not_found' | 'join_to_viewIssueTermRefresh' | 'open_issue_in_jiraIssueTermRefresh' | 'request_access_to_viewIssueTermRefresh' | 'team_members_count' | 'status_change_permission_errorIssueTermRefresh' | 'connect_unauthorised_account_description_appify' | 'connect_unauthorised_account_description_no_provider_appify' | 'learn_more_about_connecting_account_experiment_shorter' | 'learn_more_about_connecting_account_appify' | 'rovo_summary_loading' | 'ai_disclaimer' | 'rovo_unauthorised_title' | 'rovo_unauthorised_title_no_provider' | 'rovo_unauthorised_feature_clear_link_names' | 'rovo_unauthorised_feature_understand_linked_docs' | 'rovo_unauthorised_feature_go_deeper_smart_suggestions' | 'rovo_unauthorised_connect_account' | 'rovo_unauthorised_not_now' | 'rovo_chat_action_section_header' | 'rovo_prompt_button_summarize_this' | 'rovo_prompt_button_ask_a_specific_question' | 'rovo_prompt_button_show_me_whats_relevant' | RovoChatActionMessageKey | 'pre_auth_block_social_proof_not_low' | 'pre_auth_block_social_proof_low';
5
5
  type Messages = {
6
6
  [K in MessageKey]: MessageDescriptor;
7
7
  };
@@ -1,4 +1,3 @@
1
- export { CURRENT_SITE_CLOUD_ID_LOCAL_STORAGE_KEY, CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY, currentSiteCloudIdService, getCurrentSiteCloudId, getCachedCurrentSiteCloudIdAndRefresh, } from '../../services/current-site-cloud-id';
2
1
  declare const useCurrentSiteCloudId: () => {
3
2
  cloudId: string | undefined;
4
3
  isLoading: boolean;
@@ -5,9 +5,22 @@ export interface SocialProof {
5
5
  isLoading: boolean;
6
6
  }
7
7
  /**
8
- * Fetches provider usage percentage from the TAP Delivery personalization service when the
9
- * killswitch allows it. Callers decide separately (e.g. via Statsig experiment) whether to
10
- * surface that data in the UI.
8
+ * Cache-first social proof hook.
9
+ *
10
+ * On mount:
11
+ * 1. Reads localStorage synchronously via `getProviderPctMapSync`.
12
+ * - If data exists (warm cache): sets `providerPctMap` immediately, `isLoading = false`.
13
+ * - If no data (cold cache): leaves `providerPctMap` undefined, `isLoading = false`.
14
+ * 2. Always kicks off an async fetch (fire-and-forget) via `getProviderPctMap` to populate
15
+ * localStorage for next page load. Does NOT update state with the async result.
16
+ *
17
+ * This means:
18
+ * - First page visit (cold): no social proof rendered, no experiment exposure fired.
19
+ * Background fetch populates localStorage for next time.
20
+ * - Second page visit (warm): social proof renders immediately from localStorage.
21
+ * Background refresh keeps localStorage fresh.
22
+ *
23
+ * Callers decide separately (e.g. via Statsig experiment) whether to surface the data.
11
24
  */
12
- declare const useSocialProof: (traitName: string, extensionKey?: string, isKillswitchOn?: boolean) => SocialProof;
25
+ declare const useSocialProof: (extensionKey?: string, isKillswitchOn?: boolean, baseUriWithNoTrailingSlash?: string) => SocialProof;
13
26
  export default useSocialProof;
@@ -0,0 +1,39 @@
1
+ export type SocialProofTier = 'low' | 'not-low';
2
+ export interface SocialProofExperiment {
3
+ /**
4
+ * The raw connected users percentage for the current provider, or undefined if not loaded/available.
5
+ */
6
+ connectedPct: number | undefined;
7
+ /**
8
+ * True while the trait is being fetched.
9
+ */
10
+ isLoading: boolean;
11
+ /**
12
+ * True when the user is in the treatment cohort and should see the
13
+ * social proof UI. All treatment surfaces should gate on this.
14
+ */
15
+ isTreatment: boolean;
16
+ /**
17
+ * The social proof tier based on the tenant's 3P connected users percentage.
18
+ * 'low' when tenant 3P adoption is <30%, 'not-low' for >=30%.
19
+ * Only meaningful when isTreatment is true.
20
+ */
21
+ tier: SocialProofTier;
22
+ }
23
+ /**
24
+ * Returns enrollment and treatment state for the social proof unauth block card experiment.
25
+ *
26
+ * Delegates data-fetching to `useSocialProof` (Sasha's shared service layer),
27
+ * which handles cloudId resolution, TAP trait fetch, localStorage caching,
28
+ * request deduping, and sync reads.
29
+ *
30
+ * This hook adds the experiment/cohort layer on top:
31
+ * - Only fires exposure (via editorExperiment) when data has loaded.
32
+ * - Derives `tier` and `isTreatment` for rendering decisions.
33
+ *
34
+ * Expected to be called only inside the fg-enabled branch (via componentWithFG).
35
+ *
36
+ * @param extensionKey - The extensionKey of the current 3P provider (e.g. 'google-object-provider').
37
+ */
38
+ declare const useSocialProofExperiment: (extensionKey?: string, baseUriWithNoTrailingSlash?: string) => SocialProofExperiment;
39
+ export default useSocialProofExperiment;
@@ -1,46 +1,22 @@
1
- export declare const CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY: string;
1
+ export declare const CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY_PREFIX = "site-cloud-id:v1:";
2
+ export declare const CURRENT_SITE_CLOUD_ID_TTL_MS: number;
2
3
  /** Keys written by this service in localStorage when using {@link smartCardStorage}. */
4
+ export declare const getCurrentSiteCloudIdLocalStorageKey: (baseUriWithNoTrailingSlash?: string) => string;
5
+ /** Backwards-compatible default-scope key for existing tests and external assertions. */
3
6
  export declare const CURRENT_SITE_CLOUD_ID_LOCAL_STORAGE_KEY: string;
4
7
  export declare class CurrentSiteCloudIdService {
5
- /**
6
- * Holds the shared tenant_info work: one in-flight fetch, then (on success) a settled promise for the session cloud
7
- * id so later callers never trigger another `tenant_info` in the same page lifetime (until {@link clearCache}).
8
- */
9
- private tenantInfoInflightPromise;
8
+ private tenantInfoInflightPromises;
10
9
  private readStoredCloudId;
11
10
  private writeStoredCloudId;
12
11
  private ensureTenantInfoInflightStarted;
13
- /**
14
- * Returns the currently cached cloud id synchronously and starts a background refresh.
15
- * The refresh result is persisted for future calls but is not awaited by this call.
16
- */
17
- getCachedCloudIdAndRefresh(): string | undefined;
12
+ /** Pure synchronous read scoped to the given base URI. */
13
+ getStoredCloudId(baseUriWithNoTrailingSlash?: string): string | undefined;
18
14
  /** Writes tenant cloud id for tests or callers that intentionally warm storage before edge resolves. */
19
- persistStoredCloudId(cloudId: string): void;
20
- /**
21
- * When local storage already has a tenant cloud id, it is returned immediately; a background tenant_info refresh
22
- * is still kicked off unless one is already in flight.
23
- *
24
- * Without storage, this awaits the deduped in-flight tenant_info (first concurrent caller chooses the URL;
25
- * all share one promise regardless of subsequent `baseUriWithNoTrailingSlash`).
26
- *
27
- * On network success with no cloud id, or on failure: falls back via {@link readStoredCloudId}.
28
- */
15
+ persistStoredCloudId(cloudId: string, baseUriWithNoTrailingSlash?: string): void;
29
16
  get(baseUriWithNoTrailingSlash?: string): Promise<string | undefined>;
30
17
  /** Clears session pin and persisted storage so the next {@link get} is a fresh tenant_info fetch. */
31
18
  clearCache(): void;
32
19
  }
33
20
  export declare const currentSiteCloudIdService: CurrentSiteCloudIdService;
34
- /**
35
- * Resolves the current site cloud id through the module-level {@link currentSiteCloudIdService}.
36
- * Returns a stored cloud id immediately when one exists; otherwise waits for the shared
37
- * `tenant_info` request and persists the result for subsequent cached reads.
38
- */
21
+ export declare function getCurrentSiteCloudIdSync(baseUriWithNoTrailingSlash?: string): string | undefined;
39
22
  export declare const getCurrentSiteCloudId: (baseUriWithNoTrailingSlash?: string) => Promise<string | undefined>;
40
- /**
41
- * Reads the current site cloud id from browser storage (the `site-cloud-id:v1` row) via the
42
- * module-level {@link currentSiteCloudIdService} singleton, without awaiting network work.
43
- * Calling this also starts the shared `tenant_info` refresh in the background when one is not
44
- * already running, so a later call can observe a refreshed value when available.
45
- */
46
- export declare function getCachedCurrentSiteCloudIdAndRefresh(): string | undefined;
@@ -1,23 +1,16 @@
1
1
  import type { ProviderPctMap } from './types';
2
- /**
3
- * Logical key shape: `@atlaskit/smart-card:<feature>:<schema-version>:<scope>` (see smart-card
4
- * storage conventions). {@link StorageClient} narrows the localStorage key to
5
- * `<clientKey>_<itemKey>` with `clientKey === '@atlaskit/smart-card'` and
6
- * `itemKey === 'pct-map:v1:<cloudId>:<traitName>'` (scope segments URI-encoded).
7
- */
8
- export declare const PERSONALIZATION_STORAGE_SCOPE: string;
9
- export declare const PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX: string;
2
+ export declare const PERSONALIZATION_STORAGE_SCOPE = "smart-card-social-proof";
3
+ export declare const PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = "pct-map:v1:";
4
+ export declare const PERSONALIZATION_PROVIDER_PCT_TTL_MS: number;
5
+ export declare const SOCIAL_PROOF_TRAIT_NAME = "sl_3p_connected_providers_site_pct";
10
6
  /**
11
7
  * Service for fetching site-level traits from the TAP Delivery personalization API.
12
8
  */
13
9
  export declare class PersonalizationService {
14
10
  private cache;
15
- /**
16
- * Returns the currently cached provider percentage map synchronously and starts a background refresh.
17
- * The refresh result is persisted for future calls but is not awaited by this call.
18
- */
19
- getCachedProviderPctMapAndRefresh(traitName: string): ProviderPctMap | null;
20
- getProviderPctMap(traitName: string): Promise<ProviderPctMap | undefined>;
11
+ /** Pure synchronous read for an explicit cloud id / trait pair. */
12
+ getProviderPctMapSync(cloudId: string | undefined, traitName: string): ProviderPctMap | null;
13
+ getProviderPctMap(cloudId: string | undefined, traitName: string): Promise<ProviderPctMap | undefined>;
21
14
  private readStoredProviderPctMap;
22
15
  private writeStoredProviderPctMap;
23
16
  private normalizeProviderPctMap;
@@ -26,16 +19,13 @@ export declare class PersonalizationService {
26
19
  private clearStoredProviderPctMaps;
27
20
  }
28
21
  export declare const personalizationService: PersonalizationService;
22
+ export declare const getProviderPctMap: (cloudId: string | undefined, traitName: string) => Promise<ProviderPctMap | undefined>;
23
+ export declare function getProviderPctMapSync(cloudId: string | undefined, traitName: string): ProviderPctMap | null;
29
24
  /**
30
- * Resolves the provider percentage map for a TAP Delivery trait through the module-level
31
- * {@link personalizationService}. Work is deduped per trait name for the page lifetime, and a
32
- * successful response is persisted by cloud id and trait name for later cached reads.
33
- */
34
- export declare const getProviderPctMap: (traitName: string) => Promise<ProviderPctMap | undefined>;
35
- /**
36
- * Reads the provider percentage map for a trait from browser storage via the module-level
37
- * {@link personalizationService} singleton, without awaiting network work.
38
- * Calling this also starts the trait-scoped shared refresh in the background, so a later call can
39
- * use a refreshed value when it becomes available.
25
+ * Backwards-compatible cache-first helper for inline-card social proof callers.
26
+ *
27
+ * Reads the persisted provider percentage map synchronously using the current site cloud id, then
28
+ * starts a background refresh for subsequent mounts. The async result intentionally does not affect
29
+ * the current call site, matching the warm-cache-only rendering contract.
40
30
  */
41
31
  export declare function getCachedProviderPctMapAndRefresh(traitName: string): ProviderPctMap | null;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import type { SocialProofTier } from '../../../state/hooks/use-social-proof-experiment';
3
+ export interface SocialProofMessageProps {
4
+ /** The connected users percentage for the current provider. Only shown for 'not-low' tier. */
5
+ connectedPct?: number;
6
+ /** The 3P provider display name (e.g. "OneDrive", "Google Drive"). */
7
+ providerName: string;
8
+ /** Test ID for the component. */
9
+ testId?: string;
10
+ /** The social proof tier — determines which copy variant to show. */
11
+ tier: SocialProofTier;
12
+ }
13
+ declare const SocialProofMessage: ({ tier, connectedPct, providerName, testId, }: SocialProofMessageProps) => React.JSX.Element;
14
+ export default SocialProofMessage;
@@ -1,7 +1,7 @@
1
1
  import { type MessageDescriptor } from 'react-intl';
2
2
  export type RequestAccessMessageKey = 'click_to_join' | 'click_to_join_description' | 'forbidden_description' | 'request_access' | 'request_access_description' | 'request_access_pending' | 'request_access_pending_title' | 'request_access_pending_description' | 'request_denied_description' | 'default_no_access_title' | 'direct_access_title' | 'direct_access_description' | 'direct_access' | 'access_exists_description' | 'not_found_description' | 'not_found_title';
3
3
  export type RovoChatActionMessageKey = 'rovo_prompt_context_generic' | 'rovo_prompt_context_generic_plural' | 'rovo_prompt_context_confluence_page' | 'rovo_prompt_context_confluence_page_short' | 'rovo_prompt_context_jira_work_item' | 'rovo_prompt_context_jira_work_item_short' | 'rovo_prompt_button_recommend_other_sources' | 'rovo_prompt_message_recommend_other_sources' | 'rovo_prompt_button_show_other_mentions' | 'rovo_prompt_message_show_other_mentions' | 'rovo_prompt_button_suggest_improvement' | 'rovo_prompt_message_suggest_improvement' | 'rovo_prompt_message_summarize' | 'rovo_prompt_button_ask_rovo_anything' | 'rovo_prompt_message_ask_rovo_anything' | 'rovo_prompt_button_highlight_relevant_content' | 'rovo_prompt_message_highlight_relevant_content' | 'rovo_prompt_button_identify_key_trends' | 'rovo_prompt_message_identify_key_trends' | 'rovo_prompt_button_identify_key_points' | 'rovo_prompt_message_identify_key_points' | 'rovo_prompt_button_find_open_questions' | 'rovo_prompt_message_find_open_questions' | 'rovo_prompt_button_key_highlights' | 'rovo_prompt_message_key_highlights';
4
- export type MessageKey = 'assigned_to' | 'ai_summarize' | 'change_status' | 'ai_summarized' | 'ai_summarized_abbreviation' | 'ai_summarized_info' | 'ai_summarized_info_short' | 'ai_summarizing' | 'ai_summary_error_generic' | 'ai_summary_error_acceptable_use_violation' | 'ai_summary_error_hipaa_content_detected' | 'ai_summary_error_exceeding_context_length_error' | 'ai_summary_action' | 'ai_summary_action_description' | 'automation_action_title' | 'automation_action_tooltip' | 'automation_action_icon_label' | 'automation_action_confluence_page_modal_title' | 'automation_action_confluence_page_modal_description' | 'copy_summary_action' | 'copy_summary_action_description' | 'copied_summary_action_description' | 'beta' | 'cannot_find_link' | 'compass_applied_components_count' | 'connect_link_account_card' | 'connect_link_account_card_name' | 'connect_link_account_card_description' | 'connect_unauthorised_account_action' | 'connect_inline_social_proof' | 'social_proof_inline_cta_tag_high_with_context' | 'social_proof_inline_cta_tag_high_no_context' | 'social_proof_inline_cta_tag_low_with_context' | 'connect_unauthorised_account_description' | 'connect_unauthorised_account_description_no_provider' | 'continue' | 'copy_url_to_clipboard' | 'copied_url_to_clipboard' | 'could_not_load_link' | 'download' | 'download_description' | 'download_file' | 'follow' | 'follow_project_description' | 'follow_project_descriptionGalaxia' | 'follow_project' | 'follow_goal' | 'follow_goal_description' | 'follow_project_error' | 'follow_project_errorGalaxia' | 'follow_goal_error' | 'go_back' | 'invalid_permissions' | 'invalid_permissions_description' | 'join_to_view' | 'connect_link_account' | 'created_by' | 'created_on_relative' | 'created_on_absolute' | 'check_this_link' | 'delete' | 'edit' | 'learn_more_about_smart_links' | 'learn_more_about_connecting_account' | 'loading' | 'link_safety_warning_message' | 'modified_by' | 'modified_on_relative' | 'modified_on_absolute' | 'more_actions' | 'not_found_title' | 'not_found_description' | 'open_issue_in_jira' | 'open_link_in_a_new_tab' | 'owned_by' | 'owned_by_override' | 'preview_description' | 'preview_improved' | 'preview_modal' | 'preview_panel' | 'preview_close' | 'preview_max_size' | 'preview_min_size' | 'priority_blocker' | 'priority_critical' | 'priority_high' | 'priority_highest' | 'priority_low' | 'priority_lowest' | 'priority_major' | 'priority_medium' | 'priority_minor' | 'priority_trivial' | 'priority_undefined' | 'forbidden_access' | 'pending_request' | 'read_time' | 'restricted_link' | 'request_access_to_view' | 'request_denied' | 'sent_on_relative' | 'sent_on_absolute' | 'status_change_load_error' | 'status_change_permission_error' | 'status_change_update_error' | 'try_again' | 'try_another_account' | 'unauthorised_account_description' | 'unauthorised_account_description_no_provider' | 'unauthorised_account_name' | 'unauthorised_account_name_no_provider' | 'rovo_actions_explore' | 'unassigned' | 'unfollow' | 'unfollow_project_description' | 'unfollow_project_descriptionGalaxia' | 'unfollow_project' | 'unfollow_project_error' | 'unfollow_project_errorGalaxia' | 'unfollow_goal' | 'unfollow_goal_description' | 'unfollow_goal_error' | 'user_attributes' | 'view' | 'viewIn' | 'viewOriginal' | 'actions' | 'add_account' | 'cancel' | 'close' | 'connect_to' | 'connect_account_description' | 'retry' | 'save' | 'unlink_account' | RequestAccessMessageKey | 'related' | 'generic_error_message' | 'related_links_modal_error_title' | 'related_links_modal_error_description' | 'related_links_modal_unavailable_title' | 'related_links_modal_unavailable_description' | 'related_links_modal_title' | 'related_links_view_related_urls' | 'related_links_view_related_links' | 'related_links_found_in' | 'related_links_includes_links_to' | 'related_links_not_found' | 'join_to_viewIssueTermRefresh' | 'open_issue_in_jiraIssueTermRefresh' | 'request_access_to_viewIssueTermRefresh' | 'team_members_count' | 'status_change_permission_errorIssueTermRefresh' | 'connect_unauthorised_account_description_appify' | 'connect_unauthorised_account_description_no_provider_appify' | 'learn_more_about_connecting_account_experiment_shorter' | 'learn_more_about_connecting_account_appify' | 'rovo_summary_loading' | 'ai_disclaimer' | 'rovo_unauthorised_title' | 'rovo_unauthorised_title_no_provider' | 'rovo_unauthorised_feature_clear_link_names' | 'rovo_unauthorised_feature_understand_linked_docs' | 'rovo_unauthorised_feature_go_deeper_smart_suggestions' | 'rovo_unauthorised_connect_account' | 'rovo_unauthorised_not_now' | 'rovo_chat_action_section_header' | 'rovo_prompt_button_summarize_this' | 'rovo_prompt_button_ask_a_specific_question' | 'rovo_prompt_button_show_me_whats_relevant' | RovoChatActionMessageKey;
4
+ export type MessageKey = 'assigned_to' | 'ai_summarize' | 'change_status' | 'ai_summarized' | 'ai_summarized_abbreviation' | 'ai_summarized_info' | 'ai_summarized_info_short' | 'ai_summarizing' | 'ai_summary_error_generic' | 'ai_summary_error_acceptable_use_violation' | 'ai_summary_error_hipaa_content_detected' | 'ai_summary_error_exceeding_context_length_error' | 'ai_summary_action' | 'ai_summary_action_description' | 'automation_action_title' | 'automation_action_tooltip' | 'automation_action_icon_label' | 'automation_action_confluence_page_modal_title' | 'automation_action_confluence_page_modal_description' | 'copy_summary_action' | 'copy_summary_action_description' | 'copied_summary_action_description' | 'beta' | 'cannot_find_link' | 'compass_applied_components_count' | 'connect_link_account_card' | 'connect_link_account_card_name' | 'connect_link_account_card_description' | 'connect_unauthorised_account_action' | 'connect_inline_social_proof' | 'social_proof_inline_cta_tag_high_with_context' | 'social_proof_inline_cta_tag_high_no_context' | 'social_proof_inline_cta_tag_low_with_context' | 'connect_unauthorised_account_description' | 'connect_unauthorised_account_description_no_provider' | 'continue' | 'copy_url_to_clipboard' | 'copied_url_to_clipboard' | 'could_not_load_link' | 'download' | 'download_description' | 'download_file' | 'follow' | 'follow_project_description' | 'follow_project_descriptionGalaxia' | 'follow_project' | 'follow_goal' | 'follow_goal_description' | 'follow_project_error' | 'follow_project_errorGalaxia' | 'follow_goal_error' | 'go_back' | 'invalid_permissions' | 'invalid_permissions_description' | 'join_to_view' | 'connect_link_account' | 'created_by' | 'created_on_relative' | 'created_on_absolute' | 'check_this_link' | 'delete' | 'edit' | 'learn_more_about_smart_links' | 'learn_more_about_connecting_account' | 'loading' | 'link_safety_warning_message' | 'modified_by' | 'modified_on_relative' | 'modified_on_absolute' | 'more_actions' | 'not_found_title' | 'not_found_description' | 'open_issue_in_jira' | 'open_link_in_a_new_tab' | 'owned_by' | 'owned_by_override' | 'preview_description' | 'preview_improved' | 'preview_modal' | 'preview_panel' | 'preview_close' | 'preview_max_size' | 'preview_min_size' | 'priority_blocker' | 'priority_critical' | 'priority_high' | 'priority_highest' | 'priority_low' | 'priority_lowest' | 'priority_major' | 'priority_medium' | 'priority_minor' | 'priority_trivial' | 'priority_undefined' | 'forbidden_access' | 'pending_request' | 'read_time' | 'restricted_link' | 'request_access_to_view' | 'request_denied' | 'sent_on_relative' | 'sent_on_absolute' | 'status_change_load_error' | 'status_change_permission_error' | 'status_change_update_error' | 'try_again' | 'try_another_account' | 'unauthorised_account_description' | 'unauthorised_account_description_no_provider' | 'unauthorised_account_name' | 'unauthorised_account_name_no_provider' | 'rovo_actions_explore' | 'unassigned' | 'unfollow' | 'unfollow_project_description' | 'unfollow_project_descriptionGalaxia' | 'unfollow_project' | 'unfollow_project_error' | 'unfollow_project_errorGalaxia' | 'unfollow_goal' | 'unfollow_goal_description' | 'unfollow_goal_error' | 'user_attributes' | 'view' | 'viewIn' | 'viewOriginal' | 'actions' | 'add_account' | 'cancel' | 'close' | 'connect_to' | 'connect_account_description' | 'retry' | 'save' | 'unlink_account' | RequestAccessMessageKey | 'related' | 'generic_error_message' | 'related_links_modal_error_title' | 'related_links_modal_error_description' | 'related_links_modal_unavailable_title' | 'related_links_modal_unavailable_description' | 'related_links_modal_title' | 'related_links_view_related_urls' | 'related_links_view_related_links' | 'related_links_found_in' | 'related_links_includes_links_to' | 'related_links_not_found' | 'join_to_viewIssueTermRefresh' | 'open_issue_in_jiraIssueTermRefresh' | 'request_access_to_viewIssueTermRefresh' | 'team_members_count' | 'status_change_permission_errorIssueTermRefresh' | 'connect_unauthorised_account_description_appify' | 'connect_unauthorised_account_description_no_provider_appify' | 'learn_more_about_connecting_account_experiment_shorter' | 'learn_more_about_connecting_account_appify' | 'rovo_summary_loading' | 'ai_disclaimer' | 'rovo_unauthorised_title' | 'rovo_unauthorised_title_no_provider' | 'rovo_unauthorised_feature_clear_link_names' | 'rovo_unauthorised_feature_understand_linked_docs' | 'rovo_unauthorised_feature_go_deeper_smart_suggestions' | 'rovo_unauthorised_connect_account' | 'rovo_unauthorised_not_now' | 'rovo_chat_action_section_header' | 'rovo_prompt_button_summarize_this' | 'rovo_prompt_button_ask_a_specific_question' | 'rovo_prompt_button_show_me_whats_relevant' | RovoChatActionMessageKey | 'pre_auth_block_social_proof_not_low' | 'pre_auth_block_social_proof_low';
5
5
  type Messages = {
6
6
  [K in MessageKey]: MessageDescriptor;
7
7
  };
@@ -1,4 +1,3 @@
1
- export { CURRENT_SITE_CLOUD_ID_LOCAL_STORAGE_KEY, CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY, currentSiteCloudIdService, getCurrentSiteCloudId, getCachedCurrentSiteCloudIdAndRefresh, } from '../../services/current-site-cloud-id';
2
1
  declare const useCurrentSiteCloudId: () => {
3
2
  cloudId: string | undefined;
4
3
  isLoading: boolean;
@@ -5,9 +5,22 @@ export interface SocialProof {
5
5
  isLoading: boolean;
6
6
  }
7
7
  /**
8
- * Fetches provider usage percentage from the TAP Delivery personalization service when the
9
- * killswitch allows it. Callers decide separately (e.g. via Statsig experiment) whether to
10
- * surface that data in the UI.
8
+ * Cache-first social proof hook.
9
+ *
10
+ * On mount:
11
+ * 1. Reads localStorage synchronously via `getProviderPctMapSync`.
12
+ * - If data exists (warm cache): sets `providerPctMap` immediately, `isLoading = false`.
13
+ * - If no data (cold cache): leaves `providerPctMap` undefined, `isLoading = false`.
14
+ * 2. Always kicks off an async fetch (fire-and-forget) via `getProviderPctMap` to populate
15
+ * localStorage for next page load. Does NOT update state with the async result.
16
+ *
17
+ * This means:
18
+ * - First page visit (cold): no social proof rendered, no experiment exposure fired.
19
+ * Background fetch populates localStorage for next time.
20
+ * - Second page visit (warm): social proof renders immediately from localStorage.
21
+ * Background refresh keeps localStorage fresh.
22
+ *
23
+ * Callers decide separately (e.g. via Statsig experiment) whether to surface the data.
11
24
  */
12
- declare const useSocialProof: (traitName: string, extensionKey?: string, isKillswitchOn?: boolean) => SocialProof;
25
+ declare const useSocialProof: (extensionKey?: string, isKillswitchOn?: boolean, baseUriWithNoTrailingSlash?: string) => SocialProof;
13
26
  export default useSocialProof;
@@ -0,0 +1,39 @@
1
+ export type SocialProofTier = 'low' | 'not-low';
2
+ export interface SocialProofExperiment {
3
+ /**
4
+ * The raw connected users percentage for the current provider, or undefined if not loaded/available.
5
+ */
6
+ connectedPct: number | undefined;
7
+ /**
8
+ * True while the trait is being fetched.
9
+ */
10
+ isLoading: boolean;
11
+ /**
12
+ * True when the user is in the treatment cohort and should see the
13
+ * social proof UI. All treatment surfaces should gate on this.
14
+ */
15
+ isTreatment: boolean;
16
+ /**
17
+ * The social proof tier based on the tenant's 3P connected users percentage.
18
+ * 'low' when tenant 3P adoption is <30%, 'not-low' for >=30%.
19
+ * Only meaningful when isTreatment is true.
20
+ */
21
+ tier: SocialProofTier;
22
+ }
23
+ /**
24
+ * Returns enrollment and treatment state for the social proof unauth block card experiment.
25
+ *
26
+ * Delegates data-fetching to `useSocialProof` (Sasha's shared service layer),
27
+ * which handles cloudId resolution, TAP trait fetch, localStorage caching,
28
+ * request deduping, and sync reads.
29
+ *
30
+ * This hook adds the experiment/cohort layer on top:
31
+ * - Only fires exposure (via editorExperiment) when data has loaded.
32
+ * - Derives `tier` and `isTreatment` for rendering decisions.
33
+ *
34
+ * Expected to be called only inside the fg-enabled branch (via componentWithFG).
35
+ *
36
+ * @param extensionKey - The extensionKey of the current 3P provider (e.g. 'google-object-provider').
37
+ */
38
+ declare const useSocialProofExperiment: (extensionKey?: string, baseUriWithNoTrailingSlash?: string) => SocialProofExperiment;
39
+ export default useSocialProofExperiment;
@@ -1,46 +1,22 @@
1
- export declare const CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY: string;
1
+ export declare const CURRENT_SITE_CLOUD_ID_STORAGE_ITEM_KEY_PREFIX = "site-cloud-id:v1:";
2
+ export declare const CURRENT_SITE_CLOUD_ID_TTL_MS: number;
2
3
  /** Keys written by this service in localStorage when using {@link smartCardStorage}. */
4
+ export declare const getCurrentSiteCloudIdLocalStorageKey: (baseUriWithNoTrailingSlash?: string) => string;
5
+ /** Backwards-compatible default-scope key for existing tests and external assertions. */
3
6
  export declare const CURRENT_SITE_CLOUD_ID_LOCAL_STORAGE_KEY: string;
4
7
  export declare class CurrentSiteCloudIdService {
5
- /**
6
- * Holds the shared tenant_info work: one in-flight fetch, then (on success) a settled promise for the session cloud
7
- * id so later callers never trigger another `tenant_info` in the same page lifetime (until {@link clearCache}).
8
- */
9
- private tenantInfoInflightPromise;
8
+ private tenantInfoInflightPromises;
10
9
  private readStoredCloudId;
11
10
  private writeStoredCloudId;
12
11
  private ensureTenantInfoInflightStarted;
13
- /**
14
- * Returns the currently cached cloud id synchronously and starts a background refresh.
15
- * The refresh result is persisted for future calls but is not awaited by this call.
16
- */
17
- getCachedCloudIdAndRefresh(): string | undefined;
12
+ /** Pure synchronous read scoped to the given base URI. */
13
+ getStoredCloudId(baseUriWithNoTrailingSlash?: string): string | undefined;
18
14
  /** Writes tenant cloud id for tests or callers that intentionally warm storage before edge resolves. */
19
- persistStoredCloudId(cloudId: string): void;
20
- /**
21
- * When local storage already has a tenant cloud id, it is returned immediately; a background tenant_info refresh
22
- * is still kicked off unless one is already in flight.
23
- *
24
- * Without storage, this awaits the deduped in-flight tenant_info (first concurrent caller chooses the URL;
25
- * all share one promise regardless of subsequent `baseUriWithNoTrailingSlash`).
26
- *
27
- * On network success with no cloud id, or on failure: falls back via {@link readStoredCloudId}.
28
- */
15
+ persistStoredCloudId(cloudId: string, baseUriWithNoTrailingSlash?: string): void;
29
16
  get(baseUriWithNoTrailingSlash?: string): Promise<string | undefined>;
30
17
  /** Clears session pin and persisted storage so the next {@link get} is a fresh tenant_info fetch. */
31
18
  clearCache(): void;
32
19
  }
33
20
  export declare const currentSiteCloudIdService: CurrentSiteCloudIdService;
34
- /**
35
- * Resolves the current site cloud id through the module-level {@link currentSiteCloudIdService}.
36
- * Returns a stored cloud id immediately when one exists; otherwise waits for the shared
37
- * `tenant_info` request and persists the result for subsequent cached reads.
38
- */
21
+ export declare function getCurrentSiteCloudIdSync(baseUriWithNoTrailingSlash?: string): string | undefined;
39
22
  export declare const getCurrentSiteCloudId: (baseUriWithNoTrailingSlash?: string) => Promise<string | undefined>;
40
- /**
41
- * Reads the current site cloud id from browser storage (the `site-cloud-id:v1` row) via the
42
- * module-level {@link currentSiteCloudIdService} singleton, without awaiting network work.
43
- * Calling this also starts the shared `tenant_info` refresh in the background when one is not
44
- * already running, so a later call can observe a refreshed value when available.
45
- */
46
- export declare function getCachedCurrentSiteCloudIdAndRefresh(): string | undefined;
@@ -1,23 +1,16 @@
1
1
  import type { ProviderPctMap } from './types';
2
- /**
3
- * Logical key shape: `@atlaskit/smart-card:<feature>:<schema-version>:<scope>` (see smart-card
4
- * storage conventions). {@link StorageClient} narrows the localStorage key to
5
- * `<clientKey>_<itemKey>` with `clientKey === '@atlaskit/smart-card'` and
6
- * `itemKey === 'pct-map:v1:<cloudId>:<traitName>'` (scope segments URI-encoded).
7
- */
8
- export declare const PERSONALIZATION_STORAGE_SCOPE: string;
9
- export declare const PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX: string;
2
+ export declare const PERSONALIZATION_STORAGE_SCOPE = "smart-card-social-proof";
3
+ export declare const PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = "pct-map:v1:";
4
+ export declare const PERSONALIZATION_PROVIDER_PCT_TTL_MS: number;
5
+ export declare const SOCIAL_PROOF_TRAIT_NAME = "sl_3p_connected_providers_site_pct";
10
6
  /**
11
7
  * Service for fetching site-level traits from the TAP Delivery personalization API.
12
8
  */
13
9
  export declare class PersonalizationService {
14
10
  private cache;
15
- /**
16
- * Returns the currently cached provider percentage map synchronously and starts a background refresh.
17
- * The refresh result is persisted for future calls but is not awaited by this call.
18
- */
19
- getCachedProviderPctMapAndRefresh(traitName: string): ProviderPctMap | null;
20
- getProviderPctMap(traitName: string): Promise<ProviderPctMap | undefined>;
11
+ /** Pure synchronous read for an explicit cloud id / trait pair. */
12
+ getProviderPctMapSync(cloudId: string | undefined, traitName: string): ProviderPctMap | null;
13
+ getProviderPctMap(cloudId: string | undefined, traitName: string): Promise<ProviderPctMap | undefined>;
21
14
  private readStoredProviderPctMap;
22
15
  private writeStoredProviderPctMap;
23
16
  private normalizeProviderPctMap;
@@ -26,16 +19,13 @@ export declare class PersonalizationService {
26
19
  private clearStoredProviderPctMaps;
27
20
  }
28
21
  export declare const personalizationService: PersonalizationService;
22
+ export declare const getProviderPctMap: (cloudId: string | undefined, traitName: string) => Promise<ProviderPctMap | undefined>;
23
+ export declare function getProviderPctMapSync(cloudId: string | undefined, traitName: string): ProviderPctMap | null;
29
24
  /**
30
- * Resolves the provider percentage map for a TAP Delivery trait through the module-level
31
- * {@link personalizationService}. Work is deduped per trait name for the page lifetime, and a
32
- * successful response is persisted by cloud id and trait name for later cached reads.
33
- */
34
- export declare const getProviderPctMap: (traitName: string) => Promise<ProviderPctMap | undefined>;
35
- /**
36
- * Reads the provider percentage map for a trait from browser storage via the module-level
37
- * {@link personalizationService} singleton, without awaiting network work.
38
- * Calling this also starts the trait-scoped shared refresh in the background, so a later call can
39
- * use a refreshed value when it becomes available.
25
+ * Backwards-compatible cache-first helper for inline-card social proof callers.
26
+ *
27
+ * Reads the persisted provider percentage map synchronously using the current site cloud id, then
28
+ * starts a background refresh for subsequent mounts. The async result intentionally does not affect
29
+ * the current call site, matching the warm-cache-only rendering contract.
40
30
  */
41
31
  export declare function getCachedProviderPctMapAndRefresh(traitName: string): ProviderPctMap | null;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import type { SocialProofTier } from '../../../state/hooks/use-social-proof-experiment';
3
+ export interface SocialProofMessageProps {
4
+ /** The connected users percentage for the current provider. Only shown for 'not-low' tier. */
5
+ connectedPct?: number;
6
+ /** The 3P provider display name (e.g. "OneDrive", "Google Drive"). */
7
+ providerName: string;
8
+ /** Test ID for the component. */
9
+ testId?: string;
10
+ /** The social proof tier — determines which copy variant to show. */
11
+ tier: SocialProofTier;
12
+ }
13
+ declare const SocialProofMessage: ({ tier, connectedPct, providerName, testId, }: SocialProofMessageProps) => React.JSX.Element;
14
+ export default SocialProofMessage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/smart-card",
3
- "version": "44.7.0",
3
+ "version": "44.7.2",
4
4
  "description": "Smart card component",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -52,9 +52,9 @@
52
52
  "@atlaskit/form": "^15.5.0",
53
53
  "@atlaskit/frontend-utilities": "^3.2.0",
54
54
  "@atlaskit/heading": "^5.4.0",
55
- "@atlaskit/icon": "^34.3.0",
55
+ "@atlaskit/icon": "^34.4.0",
56
56
  "@atlaskit/icon-file-type": "^7.0.0",
57
- "@atlaskit/icon-lab": "^6.6.0",
57
+ "@atlaskit/icon-lab": "^6.7.0",
58
58
  "@atlaskit/image": "^3.0.0",
59
59
  "@atlaskit/json-ld-types": "^1.4.0",
60
60
  "@atlaskit/link": "^3.4.0",
@@ -85,7 +85,7 @@
85
85
  "@atlaskit/textfield": "^8.3.0",
86
86
  "@atlaskit/theme": "^23.2.0",
87
87
  "@atlaskit/tile": "^1.1.0",
88
- "@atlaskit/tmp-editor-statsig": "^75.2.0",
88
+ "@atlaskit/tmp-editor-statsig": "^77.0.0",
89
89
  "@atlaskit/tokens": "^13.0.0",
90
90
  "@atlaskit/tooltip": "^22.0.0",
91
91
  "@atlaskit/ufo": "^0.4.0",
@@ -256,6 +256,12 @@
256
256
  },
257
257
  "platform_sl_3p_auth_rovo_block_jira_kill_switch": {
258
258
  "type": "boolean"
259
+ },
260
+ "social-proof-3p-unauth-block-fg": {
261
+ "type": "boolean"
262
+ },
263
+ "platform_sl_incoming_outgoing_tenant_info_killswitch": {
264
+ "type": "boolean"
259
265
  }
260
266
  },
261
267
  "compassUnitTestMetricSourceId": "ari:cloud:compass:a436116f-02ce-4520-8fbb-7301462a1674:metric-source/c5751cc6-3513-4070-9deb-af31e86aed34/f74ef1bc-7240-4aac-9dc8-9dc43b502089"
@@ -20,7 +20,7 @@ const documentation: ComponentStructuredContentSource[] = [
20
20
  import: {
21
21
  name: 'Card',
22
22
  package: '@atlaskit/smart-card',
23
- type: 'default',
23
+ type: 'named',
24
24
  packagePath,
25
25
  packageJson,
26
26
  },
@@ -70,7 +70,7 @@ const documentation: ComponentStructuredContentSource[] = [
70
70
  import: {
71
71
  name: 'HoverCard',
72
72
  package: '@atlaskit/smart-card/hover-card',
73
- type: 'default',
73
+ type: 'named',
74
74
  packagePath,
75
75
  packageJson,
76
76
  },