@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.
- package/CHANGELOG.md +20 -0
- package/dist/cjs/messages.js +10 -0
- package/dist/cjs/state/hooks/use-incoming-outgoing-links/index.js +100 -0
- package/dist/cjs/state/hooks/use-incoming-outgoing-links/query.js +11 -0
- package/dist/cjs/state/hooks/use-resolve/index.js +15 -120
- package/dist/cjs/state/hooks/use-response/index.js +132 -0
- package/dist/cjs/utils/analytics/analytics.js +1 -1
- package/dist/cjs/view/LinkUrl/index.js +1 -1
- package/dist/cjs/view/RelatedLinksModal/components/EmptyState.js +41 -0
- package/dist/cjs/view/RelatedLinksModal/views/errored/error-svg/index.js +62 -0
- package/dist/cjs/view/RelatedLinksModal/views/errored/index.js +22 -0
- package/dist/es2019/messages.js +10 -0
- package/dist/es2019/state/hooks/use-incoming-outgoing-links/index.js +44 -0
- package/dist/es2019/state/hooks/use-incoming-outgoing-links/query.js +23 -0
- package/dist/es2019/state/hooks/use-resolve/index.js +9 -112
- package/dist/es2019/state/hooks/use-response/index.js +126 -0
- package/dist/es2019/utils/analytics/analytics.js +1 -1
- package/dist/es2019/view/LinkUrl/index.js +1 -1
- package/dist/es2019/view/RelatedLinksModal/components/EmptyState.js +35 -0
- package/dist/es2019/view/RelatedLinksModal/views/errored/error-svg/index.js +54 -0
- package/dist/es2019/view/RelatedLinksModal/views/errored/index.js +13 -0
- package/dist/esm/messages.js +10 -0
- package/dist/esm/state/hooks/use-incoming-outgoing-links/index.js +93 -0
- package/dist/esm/state/hooks/use-incoming-outgoing-links/query.js +5 -0
- package/dist/esm/state/hooks/use-resolve/index.js +13 -118
- package/dist/esm/state/hooks/use-response/index.js +126 -0
- package/dist/esm/utils/analytics/analytics.js +1 -1
- package/dist/esm/view/LinkUrl/index.js +1 -1
- package/dist/esm/view/RelatedLinksModal/components/EmptyState.js +34 -0
- package/dist/esm/view/RelatedLinksModal/views/errored/error-svg/index.js +54 -0
- package/dist/esm/view/RelatedLinksModal/views/errored/index.js +15 -0
- package/dist/types/messages.d.ts +1 -1
- package/dist/types/state/hooks/use-incoming-outgoing-links/index.d.ts +9 -0
- package/dist/types/state/hooks/use-incoming-outgoing-links/query.d.ts +5 -0
- package/dist/types/state/hooks/use-response/index.d.ts +7 -0
- package/dist/types/view/RelatedLinksModal/components/EmptyState.d.ts +9 -0
- package/dist/types/view/RelatedLinksModal/views/errored/error-svg/index.d.ts +3 -0
- package/dist/types/view/RelatedLinksModal/views/errored/index.d.ts +2 -0
- package/dist/types-ts4.5/messages.d.ts +1 -1
- package/dist/types-ts4.5/state/hooks/use-incoming-outgoing-links/index.d.ts +9 -0
- package/dist/types-ts4.5/state/hooks/use-incoming-outgoing-links/query.d.ts +5 -0
- package/dist/types-ts4.5/state/hooks/use-response/index.d.ts +7 -0
- package/dist/types-ts4.5/view/RelatedLinksModal/components/EmptyState.d.ts +9 -0
- package/dist/types-ts4.5/view/RelatedLinksModal/views/errored/error-svg/index.d.ts +3 -0
- package/dist/types-ts4.5/view/RelatedLinksModal/views/errored/index.d.ts +2 -0
- 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,
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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.
|
|
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.
|
|
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
|
+
};
|
package/dist/esm/messages.js
CHANGED
|
@@ -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 }";
|