@atlaskit/smart-card 44.24.2 → 44.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/extractors/flexible/extract-state.js +4 -1
  3. package/dist/cjs/ssr.js +14 -3
  4. package/dist/cjs/state/actions/index.js +111 -23
  5. package/dist/cjs/state/hooks/use-resolve/index.js +7 -13
  6. package/dist/cjs/state/hooks/use-response/index.js +9 -3
  7. package/dist/cjs/state/hooks/usePrefetch.js +7 -6
  8. package/dist/cjs/state/hooks/useSmartLink.js +12 -3
  9. package/dist/cjs/state/hooks-external/useSmartLinkActions.js +3 -1
  10. package/dist/cjs/utils/analytics/analytics.js +1 -1
  11. package/dist/cjs/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.js +1 -1
  12. package/dist/cjs/view/CardWithUrl/component.js +25 -3
  13. package/dist/cjs/view/CardWithUrl/loader.js +7 -1
  14. package/dist/cjs/view/EmbedCard/useEmbedResolvePostMessageListener.js +4 -1
  15. package/dist/cjs/view/FlexibleCard/components/actions/action/server-action/index.js +5 -1
  16. package/dist/cjs/view/FlexibleCard/components/elements/common/base-lozenge-element/lozenge-action/index.js +5 -1
  17. package/dist/cjs/view/HoverCard/components/HoverCardComponent.js +2 -1
  18. package/dist/cjs/view/LinkUrl/index.js +1 -1
  19. package/dist/es2019/extractors/flexible/extract-state.js +4 -1
  20. package/dist/es2019/ssr.js +14 -3
  21. package/dist/es2019/state/actions/index.js +85 -12
  22. package/dist/es2019/state/hooks/use-resolve/index.js +11 -2
  23. package/dist/es2019/state/hooks/use-response/index.js +9 -5
  24. package/dist/es2019/state/hooks/usePrefetch.js +8 -7
  25. package/dist/es2019/state/hooks/useSmartLink.js +13 -3
  26. package/dist/es2019/state/hooks-external/useSmartLinkActions.js +3 -1
  27. package/dist/es2019/utils/analytics/analytics.js +1 -1
  28. package/dist/es2019/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.js +1 -1
  29. package/dist/es2019/view/CardWithUrl/component.js +28 -4
  30. package/dist/es2019/view/CardWithUrl/loader.js +7 -1
  31. package/dist/es2019/view/EmbedCard/useEmbedResolvePostMessageListener.js +4 -1
  32. package/dist/es2019/view/FlexibleCard/components/actions/action/server-action/index.js +5 -1
  33. package/dist/es2019/view/FlexibleCard/components/elements/common/base-lozenge-element/lozenge-action/index.js +5 -1
  34. package/dist/es2019/view/HoverCard/components/HoverCardComponent.js +2 -1
  35. package/dist/es2019/view/LinkUrl/index.js +1 -1
  36. package/dist/esm/extractors/flexible/extract-state.js +4 -1
  37. package/dist/esm/ssr.js +14 -3
  38. package/dist/esm/state/actions/index.js +111 -23
  39. package/dist/esm/state/hooks/use-resolve/index.js +7 -13
  40. package/dist/esm/state/hooks/use-response/index.js +9 -3
  41. package/dist/esm/state/hooks/usePrefetch.js +7 -6
  42. package/dist/esm/state/hooks/useSmartLink.js +13 -3
  43. package/dist/esm/state/hooks-external/useSmartLinkActions.js +3 -1
  44. package/dist/esm/utils/analytics/analytics.js +1 -1
  45. package/dist/esm/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.js +1 -1
  46. package/dist/esm/view/CardWithUrl/component.js +26 -4
  47. package/dist/esm/view/CardWithUrl/loader.js +7 -1
  48. package/dist/esm/view/EmbedCard/useEmbedResolvePostMessageListener.js +4 -1
  49. package/dist/esm/view/FlexibleCard/components/actions/action/server-action/index.js +5 -1
  50. package/dist/esm/view/FlexibleCard/components/elements/common/base-lozenge-element/lozenge-action/index.js +5 -1
  51. package/dist/esm/view/HoverCard/components/HoverCardComponent.js +2 -1
  52. package/dist/esm/view/LinkUrl/index.js +1 -1
  53. package/dist/types/state/actions/index.d.ts +3 -2
  54. package/dist/types/state/hooks/use-resolve/index.d.ts +9 -1
  55. package/dist/types/state/hooks/use-response/index.d.ts +2 -2
  56. package/dist/types/state/hooks/usePrefetch.d.ts +2 -1
  57. package/dist/types/state/hooks/useSmartLink.d.ts +14 -6
  58. package/dist/types-ts4.5/state/actions/index.d.ts +3 -2
  59. package/dist/types-ts4.5/state/hooks/use-resolve/index.d.ts +9 -1
  60. package/dist/types-ts4.5/state/hooks/use-response/index.d.ts +2 -2
  61. package/dist/types-ts4.5/state/hooks/usePrefetch.d.ts +2 -1
  62. package/dist/types-ts4.5/state/hooks/useSmartLink.d.ts +14 -6
  63. package/package.json +6 -3
@@ -5,6 +5,7 @@ import uuid from 'uuid';
5
5
  import { AnalyticsContext } from '@atlaskit/analytics-next';
6
6
  import { fg } from '@atlaskit/platform-feature-flags';
7
7
  import { context } from './utils/analytics/analytics';
8
+ import { isFlexibleUiCard } from './utils/flexible';
8
9
  import CardErrorBoundary from './view/CardWithUrl/card-error-boundary';
9
10
  import { CardWithUrl, CardWithUrlContent } from './view/CardWithUrl/component';
10
11
  import { LoadingCardLink } from './view/CardWithUrl/component-lazy/LoadingCardLink';
@@ -17,9 +18,14 @@ const CardSSROld = props => {
17
18
  var _props$id;
18
19
  return (_props$id = props.id) !== null && _props$id !== void 0 ? _props$id : uuid();
19
20
  });
21
+ // FlexibleCards always need full ORS data regardless of appearance prop,
22
+ // because they render custom blocks (TitleBlock etc.) requiring a complete response.
23
+ // Override appearance to 'block' for FlexibleCards when FG is enabled.
24
+ const effectiveAppearance = isFlexibleUiCard(props.children, props.ui) && fg('platform_smartlink_inline_resolve_optimization') ? 'block' : props.appearance;
20
25
  const cardProps = {
21
26
  ...props,
22
- id
27
+ id,
28
+ appearance: effectiveAppearance
23
29
  };
24
30
  const ErrorFallbackComponent = cardProps.fallbackComponent;
25
31
  const errorBoundaryFallbackComponent = () => {
@@ -28,7 +34,7 @@ const CardSSROld = props => {
28
34
  }
29
35
  return /*#__PURE__*/React.createElement(LoadingCardLink, cardProps);
30
36
  };
31
- const Component = cardProps.appearance === 'inline' ? 'span' : 'div';
37
+ const Component = effectiveAppearance === 'inline' ? 'span' : 'div';
32
38
  return /*#__PURE__*/React.createElement(AnalyticsContext, {
33
39
  data: context
34
40
  }, /*#__PURE__*/React.createElement(ErrorBoundary, {
@@ -47,9 +53,14 @@ const CardSSRNew = props => {
47
53
  var _props$id2;
48
54
  return (_props$id2 = props.id) !== null && _props$id2 !== void 0 ? _props$id2 : uuid();
49
55
  });
56
+ // FlexibleCards always need full ORS data regardless of appearance prop,
57
+ // because they render custom blocks (TitleBlock etc.) requiring a complete response.
58
+ // Override appearance to 'block' for FlexibleCards when FG is enabled.
59
+ const effectiveAppearance = isFlexibleUiCard(props.children, props.ui) && fg('platform_smartlink_inline_resolve_optimization') ? 'block' : props.appearance;
50
60
  const propsWithId = {
51
61
  ...props,
52
- id
62
+ id,
63
+ appearance: effectiveAppearance
53
64
  };
54
65
  return /*#__PURE__*/React.createElement(AnalyticsContext, {
55
66
  data: context
@@ -101,8 +101,40 @@ export const useSmartCardActions = (id, url) => {
101
101
  url
102
102
  }, undefined, undefined, metadataStatus));
103
103
  }, [dispatch, url]);
104
- const resolve = useCallback(async (resourceUrl = url, isReloading = false, isMetadataRequest = false) => resolveUrl(resourceUrl, isReloading, isMetadataRequest, id), [id, resolveUrl, url]);
105
- const register = useCallback(() => {
104
+
105
+ // Original resolve function signature kept intact for maximum safety when FG is off.
106
+ const resolve = useCallback(async (resourceUrl = url, isReloading = false, isMetadataRequest = false) => resolveUrl({
107
+ url: resourceUrl,
108
+ isReloading,
109
+ isMetadataRequest,
110
+ id
111
+ }), [id, resolveUrl, url]);
112
+
113
+ // New resolve function accepting ResolveUrlParams (minus 'id' which is closed over).
114
+ // Used when FG is enabled to pass appearance to ORS.
115
+ const resolveNew = useCallback(async (params = {}) => {
116
+ const {
117
+ url: resourceUrl = url,
118
+ isReloading = false,
119
+ isMetadataRequest = false,
120
+ appearance
121
+ } = params;
122
+ return resolveUrl({
123
+ url: resourceUrl,
124
+ isReloading,
125
+ isMetadataRequest,
126
+ id,
127
+ appearance
128
+ });
129
+ }, [id, resolveUrl, url]);
130
+
131
+ /**
132
+ * Register a smart link for resolution.
133
+ * @param appearance - Card appearance hint for ORS to optimize response payload.
134
+ * When 'inline', ORS returns minimal data (title, status).
135
+ * When 'block' or 'embed', ORS returns full data including summary.
136
+ */
137
+ const register = useCallback(appearance => {
106
138
  const {
107
139
  details
108
140
  } = getSmartLinkState();
@@ -110,31 +142,72 @@ export const useSmartCardActions = (id, url) => {
110
142
  dispatch(cardAction(ACTION_RESOLVING, {
111
143
  url
112
144
  }));
145
+ // Always set metadataStatus to 'pending' during registration to prevent
146
+ // loadMetadata from firing a duplicate concurrent fetch if a hover card
147
+ // mounts while registration is in-flight. This matches pre-PR behaviour.
113
148
  setMetadataStatus('pending');
114
149
  }
115
- return resolve();
116
- }, [getSmartLinkState, resolve, dispatch, url, setMetadataStatus]);
117
- const reload = useCallback(() => {
150
+ if (fg('platform_smartlink_inline_resolve_optimization')) {
151
+ return resolveNew({
152
+ url,
153
+ appearance
154
+ });
155
+ }
156
+ return resolve(url);
157
+ }, [getSmartLinkState, resolve, resolveNew, dispatch, url, setMetadataStatus]);
158
+ const reload = useCallback(appearance => {
118
159
  const {
119
160
  details
120
161
  } = getSmartLinkState();
121
162
  const definitionId = getDefinitionId(details);
122
- if (definitionId) {
123
- getByDefinitionId(definitionId, getState()).map(url => resolve(url, true));
163
+ if (fg('platform_smartlink_inline_resolve_optimization')) {
164
+ if (definitionId) {
165
+ getByDefinitionId(definitionId, getState()).map(reloadUrl => resolveNew({
166
+ url: reloadUrl,
167
+ isReloading: true,
168
+ appearance
169
+ }));
170
+ } else {
171
+ resolveNew({
172
+ url,
173
+ isReloading: true,
174
+ appearance
175
+ });
176
+ }
124
177
  } else {
125
- resolve(url, true);
178
+ if (definitionId) {
179
+ getByDefinitionId(definitionId, getState()).map(reloadUrl => resolve(reloadUrl, true));
180
+ } else {
181
+ resolve(url, true);
182
+ }
126
183
  }
127
- }, [getSmartLinkState, url, getState, resolve]);
184
+ }, [getSmartLinkState, url, getState, resolve, resolveNew]);
185
+
186
+ /**
187
+ * Load metadata for hover card preview.
188
+ * This always requests 'block' appearance to get full data including summary.
189
+ *
190
+ * Inline optimized and SSR-resolved links keep metadataStatus pending until hover
191
+ * requests the full block response.
192
+ */
128
193
  const loadMetadata = useCallback(() => {
129
194
  const {
130
195
  metadataStatus
131
196
  } = getSmartLinkState();
132
- //metadataStatus will be undefined for SSR links only
133
- if (metadataStatus === undefined) {
197
+ const needsBlockData = metadataStatus === undefined || metadataStatus === 'pending' && fg('platform_smartlink_inline_resolve_optimization');
198
+ if (needsBlockData) {
134
199
  setMetadataStatus('pending');
200
+ if (fg('platform_smartlink_inline_resolve_optimization')) {
201
+ // Always request 'block' appearance for hover card metadata to get full data
202
+ return resolveNew({
203
+ url,
204
+ isMetadataRequest: true,
205
+ appearance: 'block'
206
+ });
207
+ }
135
208
  return resolve(url, false, true);
136
209
  }
137
- }, [getSmartLinkState, resolve, setMetadataStatus, url]);
210
+ }, [getSmartLinkState, resolve, resolveNew, setMetadataStatus, url]);
138
211
  const authorize = useCallback(appearance => {
139
212
  const {
140
213
  details,
@@ -1,6 +1,7 @@
1
1
  import { useCallback } from 'react';
2
2
  import { isEntityPresent } from '@atlaskit/link-extractors';
3
3
  import { useSmartLinkContext } from '@atlaskit/link-provider';
4
+ import { fg } from '@atlaskit/platform-feature-flags';
4
5
  import { SmartLinkStatus } from '../../../constants';
5
6
  import { addMetadataToExperience } from '../../analytics';
6
7
  import useResponse from '../use-response';
@@ -20,7 +21,14 @@ const useResolve = () => {
20
21
  handleResolvedLinkResponse,
21
22
  handleResolvedLinkError
22
23
  } = useResponse();
23
- return useCallback(async (url, isReloading = false, isMetadataRequest = false, id = '') => {
24
+ return useCallback(async params => {
25
+ const {
26
+ url,
27
+ isReloading = false,
28
+ isMetadataRequest = false,
29
+ id = '',
30
+ appearance
31
+ } = params;
24
32
  const {
25
33
  details
26
34
  } = getState()[url] || {
@@ -29,7 +37,8 @@ const useResolve = () => {
29
37
  };
30
38
  const hasData = !!(details && details.data || isEntityPresent(details));
31
39
  if (isReloading || !hasData || isMetadataRequest) {
32
- return connections.client.fetchData(url, isReloading).then(response => handleResolvedLinkResponse(url, response, isReloading, isMetadataRequest)).catch(error => handleResolvedLinkError(url, error, undefined, isMetadataRequest));
40
+ const metadataStatus = appearance === 'inline' && !isMetadataRequest && fg('platform_smartlink_inline_resolve_optimization') ? 'pending' : undefined;
41
+ return connections.client.fetchData(url, isReloading, appearance).then(response => handleResolvedLinkResponse(url, response, isReloading, isMetadataRequest, metadataStatus)).catch(error => handleResolvedLinkError(url, error, undefined, isMetadataRequest));
33
42
  } else {
34
43
  addMetadataToExperience('smart-link-rendered', id, {
35
44
  cached: true
@@ -2,6 +2,7 @@ import { useCallback, useMemo } from 'react';
2
2
  import { unstable_batchedUpdates } from 'react-dom';
3
3
  import { useSmartLinkContext } from '@atlaskit/link-provider';
4
4
  import { ACTION_ERROR, ACTION_ERROR_FALLBACK, ACTION_RELOADING, ACTION_RESOLVED, ACTION_UPDATE_METADATA_STATUS, APIError, cardAction, getStatus } from '@atlaskit/linking-common';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
5
6
  import { SmartLinkStatus } from '../../../constants';
6
7
  import { getUnauthorizedJsonLd } from '../../../utils/jsonld';
7
8
  import { ERROR_MESSAGE_FATAL, ERROR_MESSAGE_METADATA, ERROR_MESSAGE_OAUTH } from '../../actions/constants';
@@ -74,9 +75,9 @@ const useResponse = () => {
74
75
  }
75
76
  }
76
77
  }, [getState, setMetadataStatus, dispatch]);
77
- const handleResolvedLinkSuccess = useCallback((resourceUrl, response, isReloading, isMetadataRequest) => {
78
- //if a link resolves normally, metadata will also always be resolved
79
- setMetadataStatus(resourceUrl, 'resolved');
78
+ const handleResolvedLinkSuccess = useCallback((resourceUrl, response, isReloading, isMetadataRequest, metadataStatus = 'resolved') => {
79
+ // Some optimized resolves intentionally leave metadata pending so hover can fetch it.
80
+ setMetadataStatus(resourceUrl, fg('platform_smartlink_inline_resolve_optimization') ? metadataStatus : 'resolved');
80
81
  // Dispatch Analytics and resolved card action - including unauthorized states.
81
82
  if (isReloading) {
82
83
  dispatch(cardAction(ACTION_RELOADING, {
@@ -88,7 +89,10 @@ const useResponse = () => {
88
89
  }, response, undefined, undefined, isMetadataRequest));
89
90
  }
90
91
  }, [setMetadataStatus, dispatch]);
91
- const handleResolvedLinkResponse = useCallback((resourceUrl, response, isReloading = false, isMetadataRequest) => {
92
+ const handleResolvedLinkResponse = useCallback((resourceUrl, response, isReloading = false, isMetadataRequest, metadataStatus) => {
93
+ if (!resourceUrl) {
94
+ return;
95
+ }
92
96
  const hostname = new URL(resourceUrl).hostname;
93
97
  const nextStatus = response ? getStatus(response) : 'fatal';
94
98
 
@@ -118,7 +122,7 @@ const useResponse = () => {
118
122
  * https://react-redux.js.org/api/batch
119
123
  */
120
124
  unstable_batchedUpdates(() => {
121
- handleResolvedLinkSuccess(resourceUrl, response, isReloading, isMetadataRequest);
125
+ handleResolvedLinkSuccess(resourceUrl, response, isReloading, isMetadataRequest, metadataStatus);
122
126
  });
123
127
  }, [handleResolvedLinkError, handleResolvedLinkSuccess, hasAuthFlowSupported]);
124
128
  return useMemo(() => ({
@@ -1,7 +1,8 @@
1
1
  import { useCallback } from 'react';
2
2
  import { useSmartLinkContext } from '@atlaskit/link-provider';
3
3
  import { ACTION_UPDATE_METADATA_STATUS, cardAction } from '@atlaskit/linking-common';
4
- export function usePrefetch(url) {
4
+ import { fg } from '@atlaskit/platform-feature-flags';
5
+ export function usePrefetch(url, appearance) {
5
6
  const {
6
7
  store,
7
8
  prefetchStore,
@@ -40,7 +41,7 @@ export function usePrefetch(url) {
40
41
  // requests by domain (as usual) to ensure we minimize the amount of connections
41
42
  // we create between browser -> ORS when making network requests.
42
43
  try {
43
- const response = await client.prefetchData(url);
44
+ const response = await client.prefetchData(url, appearance);
44
45
  // Once the data comes back, we put the link in the `resolved` status. This ensures
45
46
  // that when the link enters the viewport and is rendered, we immediately show it as
46
47
  // a Smart Link, rather than rendering a loading spinner -> immediate Smart Link.
@@ -50,18 +51,18 @@ export function usePrefetch(url) {
50
51
  url,
51
52
  payload: response
52
53
  });
53
- // Put the metadata in resolved state, theres no need for a pending or errored state as
54
- // we are following the same render flow as described by the comments above and below.
54
+ // Put the metadata status: 'pending' for inline appearance when FG is on, so HoverCard
55
+ // can upgrade to full block data. Otherwise 'resolved' preserves existing behaviour.
55
56
  dispatch(cardAction(ACTION_UPDATE_METADATA_STATUS, {
56
57
  url
57
- }, undefined, undefined, 'resolved'));
58
+ }, undefined, undefined, appearance === 'inline' && fg('platform_smartlink_inline_resolve_optimization') ? 'pending' : 'resolved'));
58
59
  }
59
- } catch (_err) {
60
+ } catch {
60
61
  // Do nothing, link will be retried under the hood with exponential backoff.
61
62
  // If it does not succeed even after those retries, the normal resolve flow
62
63
  // will start when the link is in view. Since we have not performed any store
63
64
  // mutations yet, the link will behave like a 'brand new' link.
64
65
  }
65
66
  }
66
- }, [store, prefetchStore, connections, url, getState, client, dispatch]);
67
+ }, [store, prefetchStore, connections, url, getState, client, dispatch, appearance]);
67
68
  }
@@ -1,10 +1,20 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { useSmartLinkContext } from '@atlaskit/link-provider';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
3
4
  import { useSmartCardActions as useSmartLinkActions } from '../actions';
4
5
  import { useSmartLinkConfig } from '../config';
5
6
  import { useSmartLinkRenderers } from '../renderers';
6
7
  import { useSmartCardState as useSmartLinkState } from '../store';
7
- export function useSmartLink(id, url) {
8
+
9
+ /**
10
+ * Hook for smart link state and actions.
11
+ * @param id - Unique identifier for the smart link
12
+ * @param url - The URL to resolve
13
+ * @param appearance - Card appearance hint for ORS to optimize response payload.
14
+ * When 'inline', ORS returns minimal data (title, status).
15
+ * When 'block' or 'embed', ORS returns full data including summary.
16
+ */
17
+ export function useSmartLink(id, url, appearance) {
8
18
  const state = useSmartLinkState(url);
9
19
  const {
10
20
  store,
@@ -17,9 +27,9 @@ export function useSmartLink(id, url) {
17
27
 
18
28
  // NB: used to propagate errors from hooks to error boundaries.
19
29
  const [error, setError] = useState(null);
20
- // Register the current card.
30
+ // Register the current card with appearance hint for optimized ORS response.
21
31
  const register = () => {
22
- actions.register().catch(err => setError(err));
32
+ actions.register(fg('platform_smartlink_inline_resolve_optimization') ? appearance : undefined).catch(err => setError(err));
23
33
  };
24
34
  // AFP-2511 TODO: Fix automatic suppressions below
25
35
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -40,7 +40,9 @@ export function useSmartLinkActions({
40
40
  details: linkState.details
41
41
  });
42
42
  if (expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') && prefetch && !linkState.details) {
43
- resolve(url);
43
+ resolve({
44
+ url
45
+ });
44
46
  }
45
47
  if (linkState.details && !(actionOptions !== null && actionOptions !== void 0 && actionOptions.hide)) {
46
48
  const actions = [];
@@ -2,7 +2,7 @@ export const ANALYTICS_CHANNEL = 'media';
2
2
  export const context = {
3
3
  componentName: 'smart-cards',
4
4
  packageName: "@atlaskit/smart-card" || '',
5
- packageVersion: "44.24.1" || ''
5
+ packageVersion: "44.24.2" || ''
6
6
  };
7
7
  export let TrackQuickActionType = /*#__PURE__*/function (TrackQuickActionType) {
8
8
  TrackQuickActionType["StatusUpdate"] = "StatusUpdate";
@@ -24,7 +24,7 @@ function LazyIntersectionObserverCardOld(props) {
24
24
  url,
25
25
  id
26
26
  } = props;
27
- const prefetch = usePrefetch(url);
27
+ const prefetch = usePrefetch(url, appearance);
28
28
  const Component = appearance === 'inline' ? 'span' : 'div';
29
29
  const ComponentObserver = Component;
30
30
  const onIntersection = useCallback((entries, observer) => {
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useMemo } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useRef } from 'react';
2
2
  import { useAnalyticsEvents as useAnalyticsEventsNext } from '@atlaskit/analytics-next';
3
3
  import { extractSmartLinkEmbed } from '@atlaskit/link-extractors';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -66,6 +66,9 @@ function Component({
66
66
  let isFlexibleUi = useMemo(() => isFlexibleUiCard(children, ui), [children, ui]);
67
67
 
68
68
  // Get state, actions for this card.
69
+ // appearance is pre-resolved by the caller (loader.tsx for Card, ssr.tsx for CardSSR):
70
+ // FlexibleCards are always passed appearance='block' when FG is on, so component.tsx
71
+ // simply consumes whatever appearance it receives.
69
72
  const {
70
73
  state,
71
74
  actions,
@@ -74,7 +77,7 @@ function Component({
74
77
  error,
75
78
  isPreviewPanelAvailable,
76
79
  openPreviewPanel
77
- } = useSmartLink(id, url);
80
+ } = useSmartLink(id, url, appearance);
78
81
  const ari = getObjectAri(state.details);
79
82
  const name = getObjectName(state.details);
80
83
  const definitionId = getDefinitionId(state.details);
@@ -261,10 +264,31 @@ function Component({
261
264
  });
262
265
  }
263
266
  }, [fire3PClickEvent, shouldFire3PClickEvent]);
267
+ const {
268
+ reload
269
+ } = actions;
264
270
  const handleAuthorize = useCallback(() => actions.authorize(appearance), [actions, appearance]);
265
271
  const handleRetry = useCallback(() => {
266
- actions.reload();
267
- }, [actions]);
272
+ reload();
273
+ }, [reload]);
274
+ const hasMounted = useRef(false);
275
+ const prevAppearance = useRef(appearance);
276
+
277
+ // When appearance changes from inline to non-inline on a mounted card
278
+ // (e.g. direct consumer changes inline → block/embed), reload with the new appearance
279
+ // so ORS can return the appropriate full data. We intentionally do NOT reload on
280
+ // block → embed or embed → block transitions since both already have full ORS data.
281
+ useEffect(() => {
282
+ if (!hasMounted.current) {
283
+ hasMounted.current = true;
284
+ prevAppearance.current = appearance;
285
+ return;
286
+ }
287
+ if (prevAppearance.current === 'inline' && appearance !== 'inline' && fg('platform_smartlink_inline_resolve_optimization')) {
288
+ reload(appearance);
289
+ }
290
+ prevAppearance.current = appearance;
291
+ }, [appearance, reload]);
268
292
  const handleInvoke = useCallback(opts => actions.invoke(opts, appearance), [actions, appearance]);
269
293
  const experimentMetaEventAttributes = useExperimentMetaEventAttributes({
270
294
  appearance,
@@ -3,6 +3,7 @@ import { ErrorBoundary } from 'react-error-boundary';
3
3
  import { di } from 'react-magnetic-di';
4
4
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
5
5
  import uuid from 'uuid';
6
+ import { fg } from '@atlaskit/platform-feature-flags';
6
7
  import { useAnalyticsEvents } from '../../common/analytics/generated/use-analytics-events';
7
8
  import { failUfoExperience, startUfoExperience } from '../../state/analytics';
8
9
  import { importWithRetry } from '../../utils';
@@ -103,10 +104,15 @@ export function CardWithURLRenderer(props) {
103
104
  const defaultFallBackComponent = () => null;
104
105
  const FallbackComponent = fallbackComponent !== null && fallbackComponent !== void 0 ? fallbackComponent : defaultFallBackComponent;
105
106
  const ErrorFallback = () => /*#__PURE__*/React.createElement(FallbackComponent, null);
107
+
108
+ // FlexibleCards always need full ORS data regardless of appearance prop,
109
+ // because they render custom blocks (TitleBlock etc.) requiring complete response.
110
+ // Override appearance to 'block' for all FlexibleCards when FG is enabled.
111
+ const effectiveAppearance = isFlexibleUi && fg('platform_smartlink_inline_resolve_optimization') ? 'block' : appearance;
106
112
  const cardWithUrlProps = {
107
113
  id,
108
114
  url,
109
- appearance,
115
+ appearance: effectiveAppearance,
110
116
  onClick,
111
117
  isSelected,
112
118
  isHovered,
@@ -13,7 +13,10 @@ export const useEmbedResolvePostMessageListener = ({
13
13
  }
14
14
  const isFromExpectedIframe = embedIframeRef && event.source === ((_embedIframeRef$curre = embedIframeRef.current) === null || _embedIframeRef$curre === void 0 ? void 0 : _embedIframeRef$curre.contentWindow);
15
15
  if (event.data === 'force-resolve-smart-link' && isFromExpectedIframe) {
16
- resolve(url, true);
16
+ resolve({
17
+ url,
18
+ isReloading: true
19
+ });
17
20
  }
18
21
  };
19
22
  window.addEventListener('message', messageCallback);
@@ -38,7 +38,11 @@ const ServerAction = ({
38
38
  smartLinkActionType
39
39
  });
40
40
  if (action.reload && action.reload.url) {
41
- await reload(action.reload.url, true, undefined, action.reload.id);
41
+ await reload({
42
+ url: action.reload.url,
43
+ isReloading: true,
44
+ id: action.reload.id
45
+ });
42
46
  }
43
47
  setIsLoading(false);
44
48
  if (onClick) {
@@ -116,7 +116,11 @@ const LozengeAction = ({
116
116
  smartLinkActionType: TrackQuickActionType.StatusUpdate
117
117
  });
118
118
  if (url) {
119
- await reload(url, true, undefined, linkId);
119
+ await reload({
120
+ url,
121
+ isReloading: true,
122
+ id: linkId
123
+ });
120
124
  }
121
125
  }
122
126
  } catch (err) {
@@ -1,5 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  import React, { useCallback, useEffect, useMemo, useRef } from 'react';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
3
4
  import Popup from '@atlaskit/popup';
4
5
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
5
6
  import { ActionName, CardDisplay } from '../../../constants';
@@ -121,7 +122,7 @@ export const HoverCardComponent = ({
121
122
  // to minimize the loading state
122
123
  const initResolve = useCallback(() => {
123
124
  // this check covers both non-SSR (status) & SSR case (metadataStatus)
124
- const isLinkUnresolved = linkState.status === 'pending' || !linkState.metadataStatus;
125
+ const isLinkUnresolved = linkState.status === 'pending' || !linkState.metadataStatus || linkState.status === 'resolved' && linkState.metadataStatus === 'pending' && fg('platform_smartlink_inline_resolve_optimization');
125
126
  if (!resolveTimeOutId.current && isLinkUnresolved) {
126
127
  resolveTimeOutId.current = setTimeout(() => {
127
128
  if (linkState.status === 'pending') {
@@ -9,7 +9,7 @@ import LinkWarningModal from './LinkWarningModal';
9
9
  import { useLinkWarningModal } from './LinkWarningModal/hooks/use-link-warning-modal';
10
10
  const PACKAGE_DATA = {
11
11
  packageName: "@atlaskit/smart-card",
12
- packageVersion: "44.24.1",
12
+ packageVersion: "44.24.2",
13
13
  componentName: 'linkUrl'
14
14
  };
15
15
  const LinkUrl = ({
@@ -44,7 +44,10 @@ var extractAction = function extractAction(response, id, actionOptions, appearan
44
44
  fireEvent: fireEvent,
45
45
  id: id,
46
46
  onClose: resolve ? function () {
47
- return url && resolve(url, true);
47
+ return url && resolve({
48
+ url: url,
49
+ isReloading: true
50
+ });
48
51
  } : undefined,
49
52
  origin: origin,
50
53
  response: response,
package/dist/esm/ssr.js CHANGED
@@ -9,6 +9,7 @@ import uuid from 'uuid';
9
9
  import { AnalyticsContext } from '@atlaskit/analytics-next';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { context } from './utils/analytics/analytics';
12
+ import { isFlexibleUiCard } from './utils/flexible';
12
13
  import CardErrorBoundary from './view/CardWithUrl/card-error-boundary';
13
14
  import { CardWithUrl, CardWithUrlContent } from './view/CardWithUrl/component';
14
15
  import { LoadingCardLink } from './view/CardWithUrl/component-lazy/LoadingCardLink';
@@ -23,8 +24,13 @@ var CardSSROld = function CardSSROld(props) {
23
24
  }),
24
25
  _useState2 = _slicedToArray(_useState, 1),
25
26
  id = _useState2[0];
27
+ // FlexibleCards always need full ORS data regardless of appearance prop,
28
+ // because they render custom blocks (TitleBlock etc.) requiring a complete response.
29
+ // Override appearance to 'block' for FlexibleCards when FG is enabled.
30
+ var effectiveAppearance = isFlexibleUiCard(props.children, props.ui) && fg('platform_smartlink_inline_resolve_optimization') ? 'block' : props.appearance;
26
31
  var cardProps = _objectSpread(_objectSpread({}, props), {}, {
27
- id: id
32
+ id: id,
33
+ appearance: effectiveAppearance
28
34
  });
29
35
  var ErrorFallbackComponent = cardProps.fallbackComponent;
30
36
  var errorBoundaryFallbackComponent = function errorBoundaryFallbackComponent() {
@@ -33,7 +39,7 @@ var CardSSROld = function CardSSROld(props) {
33
39
  }
34
40
  return /*#__PURE__*/React.createElement(LoadingCardLink, cardProps);
35
41
  };
36
- var Component = cardProps.appearance === 'inline' ? 'span' : 'div';
42
+ var Component = effectiveAppearance === 'inline' ? 'span' : 'div';
37
43
  return /*#__PURE__*/React.createElement(AnalyticsContext, {
38
44
  data: context
39
45
  }, /*#__PURE__*/React.createElement(ErrorBoundary, {
@@ -54,8 +60,13 @@ var CardSSRNew = function CardSSRNew(props) {
54
60
  }),
55
61
  _useState4 = _slicedToArray(_useState3, 1),
56
62
  id = _useState4[0];
63
+ // FlexibleCards always need full ORS data regardless of appearance prop,
64
+ // because they render custom blocks (TitleBlock etc.) requiring a complete response.
65
+ // Override appearance to 'block' for FlexibleCards when FG is enabled.
66
+ var effectiveAppearance = isFlexibleUiCard(props.children, props.ui) && fg('platform_smartlink_inline_resolve_optimization') ? 'block' : props.appearance;
57
67
  var propsWithId = _objectSpread(_objectSpread({}, props), {}, {
58
- id: id
68
+ id: id,
69
+ appearance: effectiveAppearance
59
70
  });
60
71
  return /*#__PURE__*/React.createElement(AnalyticsContext, {
61
72
  data: context