@atlaskit/smart-card 27.4.1 → 27.4.3

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 (46) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/messages.js +10 -0
  3. package/dist/cjs/state/hooks/use-incoming-outgoing-links/index.js +100 -0
  4. package/dist/cjs/state/hooks/use-incoming-outgoing-links/query.js +11 -0
  5. package/dist/cjs/state/hooks/use-resolve/index.js +15 -120
  6. package/dist/cjs/state/hooks/use-response/index.js +132 -0
  7. package/dist/cjs/utils/analytics/analytics.js +1 -1
  8. package/dist/cjs/view/LinkUrl/index.js +1 -1
  9. package/dist/cjs/view/RelatedLinksModal/components/EmptyState.js +41 -0
  10. package/dist/cjs/view/RelatedLinksModal/views/errored/error-svg/index.js +62 -0
  11. package/dist/cjs/view/RelatedLinksModal/views/errored/index.js +22 -0
  12. package/dist/es2019/messages.js +10 -0
  13. package/dist/es2019/state/hooks/use-incoming-outgoing-links/index.js +44 -0
  14. package/dist/es2019/state/hooks/use-incoming-outgoing-links/query.js +23 -0
  15. package/dist/es2019/state/hooks/use-resolve/index.js +9 -112
  16. package/dist/es2019/state/hooks/use-response/index.js +126 -0
  17. package/dist/es2019/utils/analytics/analytics.js +1 -1
  18. package/dist/es2019/view/LinkUrl/index.js +1 -1
  19. package/dist/es2019/view/RelatedLinksModal/components/EmptyState.js +35 -0
  20. package/dist/es2019/view/RelatedLinksModal/views/errored/error-svg/index.js +54 -0
  21. package/dist/es2019/view/RelatedLinksModal/views/errored/index.js +13 -0
  22. package/dist/esm/messages.js +10 -0
  23. package/dist/esm/state/hooks/use-incoming-outgoing-links/index.js +93 -0
  24. package/dist/esm/state/hooks/use-incoming-outgoing-links/query.js +5 -0
  25. package/dist/esm/state/hooks/use-resolve/index.js +13 -118
  26. package/dist/esm/state/hooks/use-response/index.js +126 -0
  27. package/dist/esm/utils/analytics/analytics.js +1 -1
  28. package/dist/esm/view/LinkUrl/index.js +1 -1
  29. package/dist/esm/view/RelatedLinksModal/components/EmptyState.js +34 -0
  30. package/dist/esm/view/RelatedLinksModal/views/errored/error-svg/index.js +54 -0
  31. package/dist/esm/view/RelatedLinksModal/views/errored/index.js +15 -0
  32. package/dist/types/messages.d.ts +1 -1
  33. package/dist/types/state/hooks/use-incoming-outgoing-links/index.d.ts +9 -0
  34. package/dist/types/state/hooks/use-incoming-outgoing-links/query.d.ts +5 -0
  35. package/dist/types/state/hooks/use-response/index.d.ts +7 -0
  36. package/dist/types/view/RelatedLinksModal/components/EmptyState.d.ts +9 -0
  37. package/dist/types/view/RelatedLinksModal/views/errored/error-svg/index.d.ts +3 -0
  38. package/dist/types/view/RelatedLinksModal/views/errored/index.d.ts +2 -0
  39. package/dist/types-ts4.5/messages.d.ts +1 -1
  40. package/dist/types-ts4.5/state/hooks/use-incoming-outgoing-links/index.d.ts +9 -0
  41. package/dist/types-ts4.5/state/hooks/use-incoming-outgoing-links/query.d.ts +5 -0
  42. package/dist/types-ts4.5/state/hooks/use-response/index.d.ts +7 -0
  43. package/dist/types-ts4.5/view/RelatedLinksModal/components/EmptyState.d.ts +9 -0
  44. package/dist/types-ts4.5/view/RelatedLinksModal/views/errored/error-svg/index.d.ts +3 -0
  45. package/dist/types-ts4.5/view/RelatedLinksModal/views/errored/index.d.ts +2 -0
  46. package/package.json +4 -6
@@ -0,0 +1,44 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { request } from '@atlaskit/linking-common';
3
+ import { queryIncomingOutgoingLinks as queryIncomingOutgoingAris } from './query';
4
+ /**
5
+ * @param baseUriWithNoTrailingSlash base url which will then be appended with /gateway/api/graphql to make requests to AGG
6
+ */
7
+ export const useIncomingOutgoingAri = (baseUriWithNoTrailingSlash = '') => {
8
+ const aggRequestCall = useCallback(async (body, headers) => request('post', baseUriWithNoTrailingSlash + '/gateway/api/graphql', body, headers, [200]), [baseUriWithNoTrailingSlash]);
9
+ const getIncomingOutgoingAris = useCallback(
10
+ /**
11
+ * Returning aris linking to (outgoing) and from (incoming) a given ari from AGS using
12
+ * https://developer.atlassian.com/cloud/ari-graph-store/relationships/content-referenced-entity/#query
13
+ * @param ari the ari for which the incoming outgoing aris are to be retrieved
14
+ * @param firstIncoming The maximum count of incoming relationships to fetch. Must not exceed 1000. This max limit is handled by AGG itself. Default is 50 (ORS batch max limit)
15
+ * @param firstOutgoing The maximum count of outgoing relationships to fetch. Must not exceed 1000. This max limit is handled by AGG itself. Default is 50 (ORS batch max limit)
16
+ *
17
+ */
18
+ async (ari, firstIncoming = 50, firstOutgoing = 50) => {
19
+ var _response$data$graphS, _response$data, _response$data$graphS2, _response$data$graphS3, _response$data$graphS4, _response$data$graphS5, _response$data$graphS6, _response$data2, _response$data2$graph, _response$data2$graph2, _response$data2$graph3, _response$data2$graph4;
20
+ const response = await aggRequestCall({
21
+ variables: {
22
+ ids: [ari],
23
+ firstIncoming,
24
+ firstOutgoing
25
+ },
26
+ query: queryIncomingOutgoingAris
27
+ });
28
+ const incomingAris = (_response$data$graphS = response === null || response === void 0 ? void 0 : (_response$data = response.data) === null || _response$data === void 0 ? void 0 : (_response$data$graphS2 = _response$data.graphStore) === null || _response$data$graphS2 === void 0 ? void 0 : (_response$data$graphS3 = _response$data$graphS2.incoming) === null || _response$data$graphS3 === void 0 ? void 0 : (_response$data$graphS4 = _response$data$graphS3.nodes) === null || _response$data$graphS4 === void 0 ? void 0 : (_response$data$graphS5 = _response$data$graphS4.map(node => {
29
+ var _node$from;
30
+ return node === null || node === void 0 ? void 0 : (_node$from = node.from) === null || _node$from === void 0 ? void 0 : _node$from.id;
31
+ })) === null || _response$data$graphS5 === void 0 ? void 0 : _response$data$graphS5.filter(id => !!id)) !== null && _response$data$graphS !== void 0 ? _response$data$graphS : [];
32
+ const outgoingAris = (_response$data$graphS6 = response === null || response === void 0 ? void 0 : (_response$data2 = response.data) === null || _response$data2 === void 0 ? void 0 : (_response$data2$graph = _response$data2.graphStore) === null || _response$data2$graph === void 0 ? void 0 : (_response$data2$graph2 = _response$data2$graph.outgoing) === null || _response$data2$graph2 === void 0 ? void 0 : (_response$data2$graph3 = _response$data2$graph2.nodes) === null || _response$data2$graph3 === void 0 ? void 0 : (_response$data2$graph4 = _response$data2$graph3.map(node => {
33
+ var _node$to;
34
+ return node === null || node === void 0 ? void 0 : (_node$to = node.to) === null || _node$to === void 0 ? void 0 : _node$to.id;
35
+ })) === null || _response$data2$graph4 === void 0 ? void 0 : _response$data2$graph4.filter(id => !!id)) !== null && _response$data$graphS6 !== void 0 ? _response$data$graphS6 : [];
36
+ return {
37
+ incomingAris,
38
+ outgoingAris
39
+ };
40
+ }, [aggRequestCall]);
41
+ return useMemo(() => ({
42
+ getIncomingOutgoingAris
43
+ }), [getIncomingOutgoingAris]);
44
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * AGG query to retrieve incoming/outgoing links from AGS
3
+ * https://developer.atlassian.com/cloud/ari-graph-store/relationships/content-referenced-entity/#batch-query
4
+ */
5
+ export const queryIncomingOutgoingLinks = `
6
+ query SmartCard_ContentReferencedEntityBatchQuery_V1($ids: [ID!]!, $firstIncoming: Int = 50, $firstOutgoing: Int = 50) {
7
+ graphStore @optIn(to: "GraphStore") {
8
+ incoming: contentReferencedEntityInverseBatch(ids: $ids, first: $firstIncoming) @optIn(to: "GraphStoreContentReferencedEntity") {
9
+ nodes {
10
+ from {
11
+ id
12
+ }
13
+ }
14
+ }
15
+ outgoing: contentReferencedEntityBatch(ids: $ids, first: $firstOutgoing) @optIn(to: "GraphStoreContentReferencedEntity") {
16
+ nodes {
17
+ to {
18
+ id
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }`;
@@ -1,127 +1,24 @@
1
1
  import { useCallback } from 'react';
2
- import { unstable_batchedUpdates } from 'react-dom';
3
2
  import { addMetadataToExperience } from '../../analytics';
4
- import { ACTION_ERROR, ACTION_ERROR_FALLBACK, ACTION_RELOADING, ACTION_RESOLVED, ACTION_UPDATE_METADATA_STATUS, APIError, cardAction, getStatus } from '@atlaskit/linking-common';
5
- import { ERROR_MESSAGE_FATAL, ERROR_MESSAGE_METADATA, ERROR_MESSAGE_OAUTH } from '../../actions/constants';
6
- import { getUnauthorizedJsonLd } from '../../../utils/jsonld';
7
3
  import { SmartLinkStatus } from '../../../constants';
8
4
  import { useSmartLinkContext } from '@atlaskit/link-provider';
5
+ import useResponse from '../use-response';
9
6
  const useResolve = () => {
10
7
  // Request JSON-LD data for the card from ORS, if it has extended
11
8
  // its cache lifespan OR there is no data for it currently. Once the data
12
- // has come back asynchronously, dispatch the resolved action for the card.
9
+ // has come back asynchronously, call the useResponse callback to
10
+ // dispatch the resolved action for the card.
13
11
  const {
14
12
  store,
15
- connections,
16
- config
13
+ connections
17
14
  } = useSmartLinkContext();
18
15
  const {
19
- getState,
20
- dispatch
16
+ getState
21
17
  } = store;
22
- const hasAuthFlowSupported = config.authFlow !== 'disabled';
23
- const setMetadataStatus = useCallback((url, metadataStatus) => {
24
- dispatch(cardAction(ACTION_UPDATE_METADATA_STATUS, {
25
- url
26
- }, undefined, undefined, metadataStatus));
27
- }, [dispatch]);
28
- const handleResolvedLinkError = useCallback((url, error, response, isMetadataRequest) => {
29
- const {
30
- details
31
- } = getState()[url] || {
32
- status: SmartLinkStatus.Pending,
33
- details: undefined
34
- };
35
- const hasData = !!(details && details.data);
36
-
37
- // If metadata request then set metadata status, return and do not alter link status
38
- if (isMetadataRequest) {
39
- setMetadataStatus(url, 'errored');
40
- return;
41
- }
42
- if (error.kind === 'fatal') {
43
- // If there's no previous data in the store for this URL, then bail
44
- // out and let the editor handle fallbacks (returns to a blue link).
45
- if (!hasData && status !== 'resolved') {
46
- dispatch(cardAction(ACTION_ERROR, {
47
- url
48
- }, details, error));
49
- throw error;
50
- }
51
- // If we already have resolved data for this URL in the store, then
52
- // simply fallback to the previous data.
53
- if (hasData) {
54
- dispatch(cardAction(ACTION_RESOLVED, {
55
- url
56
- }, details));
57
- }
58
- // Handle AuthErrors (user did not have access to resource) -
59
- // Missing AAID in ASAP claims, or missing UserContext, or 403 from downstream
60
- } else if (error.kind === 'auth') {
61
- dispatch(cardAction(ACTION_RESOLVED, {
62
- url
63
- }, getUnauthorizedJsonLd()));
64
- } else {
65
- if (error.kind === 'fallback') {
66
- // Fallback to blue link with smart link formatting. Not part of reliability.
67
- dispatch(cardAction(ACTION_ERROR_FALLBACK, {
68
- url
69
- }, response, error));
70
- } else {
71
- // Fallback to blue link with smart link formatting. Part of reliability.
72
- dispatch(cardAction(ACTION_ERROR, {
73
- url
74
- }, undefined, error));
75
- }
76
- }
77
- }, [getState, setMetadataStatus, dispatch]);
78
- const handleResolvedLinkSuccess = useCallback((resourceUrl, response, isReloading, isMetadataRequest) => {
79
- //if a link resolves normally, metadata will also always be resolved
80
- setMetadataStatus(resourceUrl, 'resolved');
81
- // Dispatch Analytics and resolved card action - including unauthorized states.
82
- if (isReloading) {
83
- dispatch(cardAction(ACTION_RELOADING, {
84
- url: resourceUrl
85
- }, response));
86
- } else {
87
- dispatch(cardAction(ACTION_RESOLVED, {
88
- url: resourceUrl
89
- }, response, undefined, undefined, isMetadataRequest));
90
- }
91
- }, [setMetadataStatus, dispatch]);
92
- const handleResolvedLinkResponse = useCallback((resourceUrl, response, isReloading = false, isMetadataRequest) => {
93
- const hostname = new URL(resourceUrl).hostname;
94
- const nextStatus = response ? getStatus(response) : 'fatal';
95
-
96
- // If we require authorization & do not have an authFlow available,
97
- // throw an error and render as a normal blue link.
98
- if ((nextStatus === 'unauthorized' || nextStatus === 'forbidden') && !hasAuthFlowSupported) {
99
- handleResolvedLinkError(resourceUrl, new APIError('fallback', hostname, ERROR_MESSAGE_OAUTH), response, isMetadataRequest);
100
- return;
101
- }
102
-
103
- // Handle any other errors
104
- if (nextStatus === 'fatal') {
105
- handleResolvedLinkError(resourceUrl, new APIError('fatal', hostname, ERROR_MESSAGE_FATAL), undefined, isMetadataRequest);
106
- return;
107
- }
108
-
109
- // Handle errors of any other kind in the metadata request response
110
- if (isMetadataRequest && nextStatus !== 'resolved') {
111
- handleResolvedLinkError(resourceUrl, new APIError('error', hostname, ERROR_MESSAGE_METADATA), response, isMetadataRequest);
112
- return;
113
- }
114
-
115
- /**
116
- * Using unstable_batchedUpdates because this store update happens async and trigers a rerender
117
- * more info:
118
- * https://github.com/facebook/react/blob/v18.2.0/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js#L113
119
- * https://react-redux.js.org/api/batch
120
- */
121
- unstable_batchedUpdates(() => {
122
- handleResolvedLinkSuccess(resourceUrl, response, isReloading, isMetadataRequest);
123
- });
124
- }, [handleResolvedLinkError, handleResolvedLinkSuccess, hasAuthFlowSupported]);
18
+ const {
19
+ handleResolvedLinkResponse,
20
+ handleResolvedLinkError
21
+ } = useResponse();
125
22
  return useCallback(async (url, isReloading = false, isMetadataRequest = false, id = '') => {
126
23
  const {
127
24
  details
@@ -0,0 +1,126 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { ACTION_ERROR, ACTION_ERROR_FALLBACK, ACTION_RELOADING, ACTION_RESOLVED, ACTION_UPDATE_METADATA_STATUS, APIError, cardAction, getStatus } from '@atlaskit/linking-common';
3
+ import { SmartLinkStatus } from '../../../constants';
4
+ import { getUnauthorizedJsonLd } from '../../../utils/jsonld';
5
+ import { ERROR_MESSAGE_FATAL, ERROR_MESSAGE_METADATA, ERROR_MESSAGE_OAUTH } from '../../actions/constants';
6
+ import { unstable_batchedUpdates } from 'react-dom';
7
+ import { useSmartLinkContext } from '@atlaskit/link-provider';
8
+ const useResponse = () => {
9
+ // Takes in the JSON-LD response and dispatch the resolved action for the card.
10
+ // It will dispatch a successful or error action
11
+ const {
12
+ store,
13
+ config
14
+ } = useSmartLinkContext();
15
+ const {
16
+ getState,
17
+ dispatch
18
+ } = store;
19
+ const hasAuthFlowSupported = config.authFlow !== 'disabled';
20
+ const setMetadataStatus = useCallback((url, metadataStatus) => {
21
+ dispatch(cardAction(ACTION_UPDATE_METADATA_STATUS, {
22
+ url
23
+ }, undefined, undefined, metadataStatus));
24
+ }, [dispatch]);
25
+ const handleResolvedLinkError = useCallback((url, error, response, isMetadataRequest) => {
26
+ const {
27
+ details
28
+ } = getState()[url] || {
29
+ status: SmartLinkStatus.Pending,
30
+ details: undefined
31
+ };
32
+ const hasData = !!(details && details.data);
33
+ // If metadata request then set metadata status, return and do not alter link status
34
+ if (isMetadataRequest) {
35
+ setMetadataStatus(url, 'errored');
36
+ return;
37
+ }
38
+ if (error.kind === 'fatal') {
39
+ // If there's no previous data in the store for this URL, then bail
40
+ // out and let the editor handle fallbacks (returns to a blue link).
41
+ if (!hasData && status !== 'resolved') {
42
+ dispatch(cardAction(ACTION_ERROR, {
43
+ url
44
+ }, details, error));
45
+ throw error;
46
+ }
47
+ // If we already have resolved data for this URL in the store, then
48
+ // simply fallback to the previous data.
49
+ if (hasData) {
50
+ dispatch(cardAction(ACTION_RESOLVED, {
51
+ url
52
+ }, details));
53
+ }
54
+ // Handle AuthErrors (user did not have access to resource) -
55
+ // Missing AAID in ASAP claims, or missing UserContext, or 403 from downstream
56
+ } else if (error.kind === 'auth') {
57
+ dispatch(cardAction(ACTION_RESOLVED, {
58
+ url
59
+ }, getUnauthorizedJsonLd()));
60
+ } else {
61
+ if (error.kind === 'fallback') {
62
+ // Fallback to blue link with smart link formatting. Not part of reliability.
63
+ dispatch(cardAction(ACTION_ERROR_FALLBACK, {
64
+ url
65
+ }, response, error));
66
+ } else {
67
+ // Fallback to blue link with smart link formatting. Part of reliability.
68
+ dispatch(cardAction(ACTION_ERROR, {
69
+ url
70
+ }, undefined, error));
71
+ }
72
+ }
73
+ }, [getState, setMetadataStatus, dispatch]);
74
+ const handleResolvedLinkSuccess = useCallback((resourceUrl, response, isReloading, isMetadataRequest) => {
75
+ //if a link resolves normally, metadata will also always be resolved
76
+ setMetadataStatus(resourceUrl, 'resolved');
77
+ // Dispatch Analytics and resolved card action - including unauthorized states.
78
+ if (isReloading) {
79
+ dispatch(cardAction(ACTION_RELOADING, {
80
+ url: resourceUrl
81
+ }, response));
82
+ } else {
83
+ dispatch(cardAction(ACTION_RESOLVED, {
84
+ url: resourceUrl
85
+ }, response, undefined, undefined, isMetadataRequest));
86
+ }
87
+ }, [setMetadataStatus, dispatch]);
88
+ const handleResolvedLinkResponse = useCallback((resourceUrl, response, isReloading = false, isMetadataRequest) => {
89
+ const hostname = new URL(resourceUrl).hostname;
90
+ const nextStatus = response ? getStatus(response) : 'fatal';
91
+
92
+ // If we require authorization & do not have an authFlow available,
93
+ // throw an error and render as a normal blue link.
94
+ if ((nextStatus === 'unauthorized' || nextStatus === 'forbidden') && !hasAuthFlowSupported) {
95
+ handleResolvedLinkError(resourceUrl, new APIError('fallback', hostname, ERROR_MESSAGE_OAUTH), response, isMetadataRequest);
96
+ return;
97
+ }
98
+
99
+ // Handle any other errors
100
+ if (nextStatus === 'fatal') {
101
+ handleResolvedLinkError(resourceUrl, new APIError('fatal', hostname, ERROR_MESSAGE_FATAL), undefined, isMetadataRequest);
102
+ return;
103
+ }
104
+
105
+ // Handle errors of any other kind in the metadata request response
106
+ if (isMetadataRequest && nextStatus !== 'resolved') {
107
+ handleResolvedLinkError(resourceUrl, new APIError('error', hostname, ERROR_MESSAGE_METADATA), response, isMetadataRequest);
108
+ return;
109
+ }
110
+
111
+ /**
112
+ * Using unstable_batchedUpdates because this store update happens async and triggers a rerender
113
+ * more info:
114
+ * https://github.com/facebook/react/blob/v18.2.0/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js#L113
115
+ * https://react-redux.js.org/api/batch
116
+ */
117
+ unstable_batchedUpdates(() => {
118
+ handleResolvedLinkSuccess(resourceUrl, response, isReloading, isMetadataRequest);
119
+ });
120
+ }, [handleResolvedLinkError, handleResolvedLinkSuccess, hasAuthFlowSupported]);
121
+ return useMemo(() => ({
122
+ handleResolvedLinkError,
123
+ handleResolvedLinkResponse
124
+ }), [handleResolvedLinkError, handleResolvedLinkResponse]);
125
+ };
126
+ export default useResponse;
@@ -4,7 +4,7 @@ export const ANALYTICS_CHANNEL = 'media';
4
4
  export const context = {
5
5
  componentName: 'smart-cards',
6
6
  packageName: "@atlaskit/smart-card",
7
- packageVersion: "27.4.1"
7
+ packageVersion: "27.4.3"
8
8
  };
9
9
  export let TrackQuickActionType = /*#__PURE__*/function (TrackQuickActionType) {
10
10
  TrackQuickActionType["StatusUpdate"] = "StatusUpdate";
@@ -7,7 +7,7 @@ import { useLinkWarningModal } from './LinkWarningModal/hooks/use-link-warning-m
7
7
  import LinkWarningModal from './LinkWarningModal';
8
8
  const PACKAGE_DATA = {
9
9
  packageName: "@atlaskit/smart-card",
10
- packageVersion: "27.4.1",
10
+ packageVersion: "27.4.3",
11
11
  componentName: 'linkUrl'
12
12
  };
13
13
  const Link = withLinkClickedEvent('a');
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import Heading from '@atlaskit/heading';
3
+ import { Box, xcss } from '@atlaskit/primitives';
4
+ const emptyStateStyles = xcss({
5
+ alignItems: "center",
6
+ display: 'flex',
7
+ flexDirection: 'column',
8
+ gap: "space.100",
9
+ height: '100%',
10
+ justifyContent: 'center',
11
+ marginLeft: 'space.300',
12
+ marginRight: 'space.300',
13
+ textAlign: 'center'
14
+ });
15
+ const descriptionStyles = xcss({
16
+ marginTop: "var(--ds-space-0, 0px)",
17
+ marginBottom: "var(--ds-space-300, 24px)",
18
+ color: 'color.text'
19
+ });
20
+ export const EmptyState = ({
21
+ testId,
22
+ header,
23
+ description,
24
+ renderImage
25
+ }) => {
26
+ return /*#__PURE__*/React.createElement(Box, {
27
+ xcss: emptyStateStyles,
28
+ testId: testId
29
+ }, renderImage === null || renderImage === void 0 ? void 0 : renderImage(), /*#__PURE__*/React.createElement(Heading, {
30
+ size: "small"
31
+ }, header), description && /*#__PURE__*/React.createElement(Box, {
32
+ as: "p",
33
+ xcss: descriptionStyles
34
+ }, description));
35
+ };
@@ -0,0 +1,54 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ /** @jsx jsx */
3
+ import { css, jsx } from '@emotion/react';
4
+ const errorStyles = css({
5
+ width: '80px',
6
+ margin: `0 auto ${"var(--ds-space-300, 24px)"}`,
7
+ display: 'block'
8
+ });
9
+ export const ErrorSVG = props => {
10
+ const id = 'related-links-error-svg';
11
+ return jsx("svg", _extends({
12
+ "data-testid": id,
13
+ viewBox: "0 0 164 212",
14
+ fill: "none",
15
+ xmlns: "http://www.w3.org/2000/svg",
16
+ css: errorStyles
17
+ }, props), jsx("path", {
18
+ d: "m95.43 74.16 66.44 115.08c5.84 10.12-1.46 22.76-13.14 22.76H15.85c-11.68 0-18.98-12.65-13.14-22.76L69.15 74.16c5.84-10.12 20.44-10.12 26.28 0Zm-7.56 83.55 2.88-44.35c.34-5.29-3.86-9.78-9.16-9.78-5.31 0-9.51 4.48-9.16 9.78l2.88 44.35a6.3 6.3 0 0 0 6.28 5.89c3.31 0 6.06-2.58 6.28-5.89Zm-15.84 23.54c0 5.66 4.76 10.1 10.39 9.58 4.85-.45 8.73-4.5 8.83-9.37.11-5.45-4.31-9.94-9.73-9.94-5.23-.01-9.49 4.37-9.49 9.73Z",
19
+ fill: `url(#${id})`
20
+ }), jsx("path", {
21
+ opacity: ".6",
22
+ d: "M93.35 27.17 85.94.45c-.17-.62-1.07-.58-1.19.05l-4.66 25.1-5.8-1.08a.612.612 0 0 0-.7.76L81 52c.17.62 1.07.58 1.19-.05l4.66-25.1 5.8 1.08c.45.08.82-.33.7-.76ZM66.77 41.81 55.9 33.26c-.22-.18-.53.07-.41.33l5.53 11.43-2.66 1.29c-.18.09-.2.33-.05.45l10.87 8.55c.22.18.53-.07.41-.33l-5.53-11.43 2.66-1.29c.17-.08.2-.33.05-.45Z",
23
+ fill: "#C1C7D0"
24
+ }), jsx("path", {
25
+ opacity: ".6",
26
+ d: "M102.78 56.75a.993.993 0 0 1-.92-1.38c-.24-.77-2.29-2.85-3.54-4.11-2.97-3.01-4.93-5-3.84-6.49 1.09-1.48 3.58-.22 7.35 1.7 1.32.67 3.35 1.7 4.45 2.03-.64-.95-2.24-2.58-3.28-3.63-2.97-3.01-4.93-5-3.84-6.49 1.08-1.48 3.58-.22 7.34 1.7 1.32.67 3.34 1.7 4.45 2.03-.64-.95-2.24-2.57-3.28-3.62-2.97-3.01-4.93-5-3.84-6.49 1.08-1.48 3.57-.22 7.34 1.7 1.58.8 4.18 2.13 4.98 2.13a1.005 1.005 0 0 1 1.46 1.36c-1.09 1.48-3.57.22-7.34-1.7-1.32-.67-3.34-1.7-4.44-2.03.64.95 2.24 2.57 3.28 3.62 2.97 3.01 4.93 5 3.84 6.49-1.08 1.48-3.57.22-7.34-1.7-1.32-.67-3.35-1.7-4.45-2.03.64.95 2.24 2.57 3.28 3.63 2.97 3.01 4.93 5 3.84 6.49-1.09 1.48-3.58.22-7.35-1.7-1.32-.67-3.35-1.71-4.45-2.03.64.95 2.24 2.58 3.28 3.63 2.97 3.01 4.93 5 3.84 6.49-.21.26-.51.4-.82.4Zm5.56 14.32c-.35 0-.69-.19-.88-.52-1.04-1.88-2.22-4.02-.91-5.21 1.31-1.2 3.34.18 5.49 1.63.87.59 2.26 1.53 2.93 1.71-.12-.68-.93-2.15-1.45-3.07-1.26-2.27-2.45-4.41-1.14-5.61 1.31-1.2 3.34.18 5.49 1.63.96.65 2.54 1.72 3.1 1.74a1 1 0 0 1 1.27 1.54c-1.31 1.2-3.34-.17-5.49-1.63-.87-.59-2.26-1.53-2.93-1.71.12.68.93 2.15 1.45 3.07 1.26 2.27 2.45 4.41 1.14 5.61-1.31 1.2-3.34-.18-5.49-1.63-.87-.59-2.26-1.53-2.93-1.71.12.68.93 2.14 1.22 2.67.27.48.09 1.09-.39 1.36-.15.09-.32.13-.48.13Zm-54.32-2.89c-.22 0-.44-.04-.67-.14-1.64-.69-1.24-3.11-.81-5.67.17-1.04.45-2.7.33-3.38-.57.39-1.56 1.75-2.18 2.6-1.53 2.09-2.99 4.07-4.62 3.38a.991.991 0 0 1-.53-1.31c.2-.48.73-.72 1.21-.57.52-.21 1.65-1.75 2.34-2.68 1.53-2.09 2.98-4.08 4.62-3.38 1.64.69 1.24 3.11.81 5.67-.17 1.04-.45 2.7-.33 3.38.57-.39 1.56-1.75 2.18-2.6 1.53-2.09 2.98-4.08 4.62-3.38 1.63.69 1.24 3.09.89 5.21-.09.54-.6.91-1.15.83-.54-.09-.92-.6-.83-1.15.1-.59.37-2.24.26-2.92-.57.39-1.56 1.75-2.18 2.6-1.33 1.79-2.59 3.51-3.96 3.51Z",
27
+ fill: "#B3BAC5"
28
+ }), jsx("defs", null, jsx("linearGradient", {
29
+ id: id,
30
+ x1: "26.169",
31
+ y1: "228.621",
32
+ x2: "138.408",
33
+ y2: "116.382",
34
+ gradientUnits: "userSpaceOnUse"
35
+ }, jsx("stop", {
36
+ stopColor: "#C1C7D0"
37
+ }), jsx("stop", {
38
+ offset: ".297",
39
+ stopColor: "#C6CBD4",
40
+ stopOpacity: ".881"
41
+ }), jsx("stop", {
42
+ offset: ".63",
43
+ stopColor: "#D3D7DE",
44
+ stopOpacity: ".748"
45
+ }), jsx("stop", {
46
+ offset: ".98",
47
+ stopColor: "#E9EBEF",
48
+ stopOpacity: ".608"
49
+ }), jsx("stop", {
50
+ offset: "1",
51
+ stopColor: "#EBECF0",
52
+ stopOpacity: ".6"
53
+ }))));
54
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { FormattedMessage, useIntl } from "react-intl-next";
3
+ import { messages } from '../../../../messages';
4
+ import { ErrorSVG } from "./error-svg";
5
+ import { EmptyState } from '../../components/EmptyState';
6
+ export const RelatedLinksErroredView = () => {
7
+ const intl = useIntl();
8
+ return /*#__PURE__*/React.createElement(EmptyState, {
9
+ renderImage: () => /*#__PURE__*/React.createElement(ErrorSVG, null),
10
+ header: intl.formatMessage(messages.related_links_modal_error_header),
11
+ description: /*#__PURE__*/React.createElement(FormattedMessage, messages.related_links_modal_error_message)
12
+ });
13
+ };
@@ -724,5 +724,15 @@ export var messages = defineMessages({
724
724
  id: 'fabric.linking.related_links_includes_links_to',
725
725
  defaultMessage: 'Includes Links To',
726
726
  description: 'Informs the user of the resources the url links to'
727
+ },
728
+ related_links_modal_error_header: {
729
+ id: 'fabric.linking.related_links_modal_error_header',
730
+ defaultMessage: 'Something went wrong',
731
+ description: 'Title for related links error'
732
+ },
733
+ related_links_modal_error_message: {
734
+ id: 'fabric.linking.related_links_modal_error_message',
735
+ defaultMessage: 'We ran into an issue trying to load recent links. Check your connection or refresh to try again.',
736
+ description: 'Informs the user that there was a problem loading related links'
727
737
  }
728
738
  });
@@ -0,0 +1,93 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
+ import { useCallback, useMemo } from 'react';
4
+ import { request } from '@atlaskit/linking-common';
5
+ import { queryIncomingOutgoingLinks as queryIncomingOutgoingAris } from './query';
6
+ /**
7
+ * @param baseUriWithNoTrailingSlash base url which will then be appended with /gateway/api/graphql to make requests to AGG
8
+ */
9
+ export var useIncomingOutgoingAri = function useIncomingOutgoingAri() {
10
+ var baseUriWithNoTrailingSlash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
11
+ var aggRequestCall = useCallback( /*#__PURE__*/function () {
12
+ var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(body, headers) {
13
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
14
+ while (1) switch (_context.prev = _context.next) {
15
+ case 0:
16
+ return _context.abrupt("return", request('post', baseUriWithNoTrailingSlash + '/gateway/api/graphql', body, headers, [200]));
17
+ case 1:
18
+ case "end":
19
+ return _context.stop();
20
+ }
21
+ }, _callee);
22
+ }));
23
+ return function (_x, _x2) {
24
+ return _ref.apply(this, arguments);
25
+ };
26
+ }(), [baseUriWithNoTrailingSlash]);
27
+ var getIncomingOutgoingAris = useCallback(
28
+ /*#__PURE__*/
29
+ /**
30
+ * Returning aris linking to (outgoing) and from (incoming) a given ari from AGS using
31
+ * https://developer.atlassian.com/cloud/ari-graph-store/relationships/content-referenced-entity/#query
32
+ * @param ari the ari for which the incoming outgoing aris are to be retrieved
33
+ * @param firstIncoming The maximum count of incoming relationships to fetch. Must not exceed 1000. This max limit is handled by AGG itself. Default is 50 (ORS batch max limit)
34
+ * @param firstOutgoing The maximum count of outgoing relationships to fetch. Must not exceed 1000. This max limit is handled by AGG itself. Default is 50 (ORS batch max limit)
35
+ *
36
+ */
37
+ function () {
38
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(ari) {
39
+ var _response$data$graphS, _response$data, _response$data$graphS2, _response$data2;
40
+ var firstIncoming,
41
+ firstOutgoing,
42
+ response,
43
+ incomingAris,
44
+ outgoingAris,
45
+ _args2 = arguments;
46
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
47
+ while (1) switch (_context2.prev = _context2.next) {
48
+ case 0:
49
+ firstIncoming = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : 50;
50
+ firstOutgoing = _args2.length > 2 && _args2[2] !== undefined ? _args2[2] : 50;
51
+ _context2.next = 4;
52
+ return aggRequestCall({
53
+ variables: {
54
+ ids: [ari],
55
+ firstIncoming: firstIncoming,
56
+ firstOutgoing: firstOutgoing
57
+ },
58
+ query: queryIncomingOutgoingAris
59
+ });
60
+ case 4:
61
+ response = _context2.sent;
62
+ incomingAris = (_response$data$graphS = response === null || response === void 0 || (_response$data = response.data) === null || _response$data === void 0 || (_response$data = _response$data.graphStore) === null || _response$data === void 0 || (_response$data = _response$data.incoming) === null || _response$data === void 0 || (_response$data = _response$data.nodes) === null || _response$data === void 0 || (_response$data = _response$data.map(function (node) {
63
+ var _node$from;
64
+ return node === null || node === void 0 || (_node$from = node.from) === null || _node$from === void 0 ? void 0 : _node$from.id;
65
+ })) === null || _response$data === void 0 ? void 0 : _response$data.filter(function (id) {
66
+ return !!id;
67
+ })) !== null && _response$data$graphS !== void 0 ? _response$data$graphS : [];
68
+ outgoingAris = (_response$data$graphS2 = response === null || response === void 0 || (_response$data2 = response.data) === null || _response$data2 === void 0 || (_response$data2 = _response$data2.graphStore) === null || _response$data2 === void 0 || (_response$data2 = _response$data2.outgoing) === null || _response$data2 === void 0 || (_response$data2 = _response$data2.nodes) === null || _response$data2 === void 0 || (_response$data2 = _response$data2.map(function (node) {
69
+ var _node$to;
70
+ return node === null || node === void 0 || (_node$to = node.to) === null || _node$to === void 0 ? void 0 : _node$to.id;
71
+ })) === null || _response$data2 === void 0 ? void 0 : _response$data2.filter(function (id) {
72
+ return !!id;
73
+ })) !== null && _response$data$graphS2 !== void 0 ? _response$data$graphS2 : [];
74
+ return _context2.abrupt("return", {
75
+ incomingAris: incomingAris,
76
+ outgoingAris: outgoingAris
77
+ });
78
+ case 8:
79
+ case "end":
80
+ return _context2.stop();
81
+ }
82
+ }, _callee2);
83
+ }));
84
+ return function (_x3) {
85
+ return _ref2.apply(this, arguments);
86
+ };
87
+ }(), [aggRequestCall]);
88
+ return useMemo(function () {
89
+ return {
90
+ getIncomingOutgoingAris: getIncomingOutgoingAris
91
+ };
92
+ }, [getIncomingOutgoingAris]);
93
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * AGG query to retrieve incoming/outgoing links from AGS
3
+ * https://developer.atlassian.com/cloud/ari-graph-store/relationships/content-referenced-entity/#batch-query
4
+ */
5
+ export var queryIncomingOutgoingLinks = "\n query SmartCard_ContentReferencedEntityBatchQuery_V1($ids: [ID!]!, $firstIncoming: Int = 50, $firstOutgoing: Int = 50) {\n graphStore @optIn(to: \"GraphStore\") {\n incoming: contentReferencedEntityInverseBatch(ids: $ids, first: $firstIncoming) @optIn(to: \"GraphStoreContentReferencedEntity\") {\n nodes {\n from {\n id\n }\n }\n }\n outgoing: contentReferencedEntityBatch(ids: $ids, first: $firstOutgoing) @optIn(to: \"GraphStoreContentReferencedEntity\") {\n nodes {\n to {\n id\n }\n }\n }\n }\n }";