UNPKG

@atlaskit/renderer

Version:
386 lines (384 loc) • 18.3 kB
import _extends from "@babel/runtime/helpers/extends"; /** * @jsxRuntime classic * @jsx jsx */ /* eslint-disable jsdoc/check-tag-names */ import { Fragment, useState, useMemo, useEffect } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 import { jsx } from '@emotion/react'; import { useSmartCardContext } from '@atlaskit/link-provider'; import { Card, getObjectAri, getObjectIconUrl, getObjectName } from '@atlaskit/smart-card'; import { isWithinPreviewPanelIFrame } from '@atlaskit/linking-common/utils'; import { useSmartLinkActions } from '@atlaskit/smart-card/hooks'; import { CardSSR } from '@atlaskit/smart-card/ssr'; import { HoverLinkOverlay, UnsupportedInline } from '@atlaskit/editor-common/ui'; import { fg } from '@atlaskit/platform-feature-flags'; import { AnalyticsContext } from '@atlaskit/analytics-next'; import { componentWithCondition } from '@atlaskit/platform-feature-flags-react'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { SmartLinkDraggable, SMART_LINK_DRAG_TYPES, SMART_LINK_APPEARANCE } from '@atlaskit/editor-smart-link-draggable'; import { useProvider } from '@atlaskit/editor-common/provider-factory'; import { CardErrorBoundary } from './fallback'; import { withSmartCardStorage } from '../../ui/SmartCardStorage'; import { getCardClickHandler } from '../utils/getCardClickHandler'; import { useInlineAnnotationProps } from '../../ui/annotations/element/useInlineAnnotationProps'; import { usePortal } from '../../ui/Renderer/PortalContext'; import { extractSmartLinkEmbed } from '@atlaskit/link-extractors'; const HoverLinkOverlayNoop = props => jsx(Fragment, null, props.children); const HoverLinkOverlayWithCondition = componentWithCondition(() => editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true }), HoverLinkOverlay, HoverLinkOverlayNoop); const OverlayWithCardContext = ({ rendererAppearance, isResolvedViewRendered, url, fireAnalyticsEvent, children }) => { var _cardContext$value2, _cardContext$value2$s, _cardContext$value3, _cardContext$value3$i, _cardContext$value4; const cardContext = useSmartCardContext(); // Note: useSmartLinkActions throws without smart card context. Using it here is safe // because we checked cardContext availability in the parent component const actions = useSmartLinkActions({ url, appearance: 'inline' }); const preview = useMemo(() => actions.find(action => action.id === 'preview-content'), [actions]); const fireHoverLabelAEP = previewType => { if (fireAnalyticsEvent) { var _cardContext$value, _urlState$details$met, _urlState$details, _urlState$details$met2, _urlState$details$met3, _urlState$details2, _urlState$details2$me; const store = cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value = cardContext.value) === null || _cardContext$value === void 0 ? void 0 : _cardContext$value.store; const urlState = store === null || store === void 0 ? void 0 : store.getState()[url || '']; fireAnalyticsEvent({ action: ACTION.CLICKED, actionSubject: ACTION_SUBJECT.SMART_LINK, actionSubjectId: ACTION_SUBJECT_ID.HOVER_LABEL, eventType: EVENT_TYPE.UI, attributes: { previewType, destinationProduct: (_urlState$details$met = urlState === null || urlState === void 0 ? void 0 : (_urlState$details = urlState.details) === null || _urlState$details === void 0 ? void 0 : (_urlState$details$met2 = _urlState$details.meta) === null || _urlState$details$met2 === void 0 ? void 0 : _urlState$details$met2.product) !== null && _urlState$details$met !== void 0 ? _urlState$details$met : null, destinationSubproduct: (_urlState$details$met3 = urlState === null || urlState === void 0 ? void 0 : (_urlState$details2 = urlState.details) === null || _urlState$details2 === void 0 ? void 0 : (_urlState$details2$me = _urlState$details2.meta) === null || _urlState$details2$me === void 0 ? void 0 : _urlState$details2$me.subproduct) !== null && _urlState$details$met3 !== void 0 ? _urlState$details$met3 : null } }); } }; const cardState = cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value2 = cardContext.value) === null || _cardContext$value2 === void 0 ? void 0 : (_cardContext$value2$s = _cardContext$value2.store) === null || _cardContext$value2$s === void 0 ? void 0 : _cardContext$value2$s.getState()[url || '']; const ari = getObjectAri(cardState === null || cardState === void 0 ? void 0 : cardState.details); const name = getObjectName(cardState === null || cardState === void 0 ? void 0 : cardState.details); const iconUrl = getObjectIconUrl(cardState === null || cardState === void 0 ? void 0 : cardState.details); // Get resolved URL from card state, fallback to original URL if not available let resolvedUrl = url; if (expValEquals('platform_hover_card_preview_panel', 'cohort', 'test')) { var _cardState$details; const cardStateUrl = cardState !== null && cardState !== void 0 && (_cardState$details = cardState.details) !== null && _cardState$details !== void 0 && _cardState$details.data && 'url' in cardState.details.data ? cardState.details.data.url : undefined; resolvedUrl = cardStateUrl || url; } const isPanelAvailable = ari && (cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value3 = cardContext.value) === null || _cardContext$value3 === void 0 ? void 0 : (_cardContext$value3$i = _cardContext$value3.isPreviewPanelAvailable) === null || _cardContext$value3$i === void 0 ? void 0 : _cardContext$value3$i.call(_cardContext$value3, { ari })); const openPreviewPanel = cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value4 = cardContext.value) === null || _cardContext$value4 === void 0 ? void 0 : _cardContext$value4.openPreviewPanel; const isPreviewPanelAvailable = Boolean(openPreviewPanel && isPanelAvailable); const isPreviewModalAvailable = Boolean(preview); const isPreviewAvailable = isPreviewModalAvailable || isPreviewPanelAvailable; const showPanelButtonIcon = isPreviewPanelAvailable ? 'panel' : isPreviewModalAvailable ? 'modal' : undefined; // When inside preview panel iframe, hide the overlay button const isInPreviewPanel = isWithinPreviewPanelIFrame(); const showPanelButton = isInPreviewPanel ? isPreviewPanelAvailable : isPreviewAvailable; const Overlay = isPreviewAvailable ? HoverLinkOverlayWithCondition : HoverLinkOverlayNoop; return jsx(Overlay, { isVisible: isResolvedViewRendered, url: url, compactPadding: rendererAppearance === 'comment', showPanelButton: showPanelButton, showPanelButtonIcon: showPanelButtonIcon // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onClick: event => { if (isPreviewPanelAvailable) { var _extractSmartLinkEmbe; // Prevent anchor default behaviour(click to open the anchor link) // When glance panel is available, let openPreviewPanel handle it event.preventDefault(); openPreviewPanel === null || openPreviewPanel === void 0 ? void 0 : openPreviewPanel({ url: resolvedUrl || '', ari: ari || '', name: name || '', iconUrl, panelData: { embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = extractSmartLinkEmbed(cardState === null || cardState === void 0 ? void 0 : cardState.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined } }); editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true }) && fireHoverLabelAEP('panel'); } else if (isPreviewModalAvailable) { event.preventDefault(); if (preview) { preview.invoke(); } editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true }) && fireHoverLabelAEP('modal'); } } }, children); }; const InlineCard = props => { var _cardContext$value5; const { url, data, eventHandlers, fireAnalyticsEvent, smartLinks, rendererAppearance, onSetLinkTarget } = props; const portal = usePortal(props); const cardContext = useSmartCardContext(); const provider = useProvider('cardProvider'); const SuspenseWrapperForUrl = smartLinks === null || smartLinks === void 0 ? void 0 : smartLinks.SuspenseWrapperForUrl; // Helper fn to conditionally wrap cards when suspense boundary is passed in via product const wrapWithSuspense = card => { if (SuspenseWrapperForUrl && url) { return jsx(SuspenseWrapperForUrl, { url: url }, card); } return card; }; const { getState: getSmartlinkState } = (cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value5 = cardContext.value) === null || _cardContext$value5 === void 0 ? void 0 : _cardContext$value5.store) || {}; const [isResolvedViewRendered, setIsResolvedViewRendered] = useState(false); const cardState = getSmartlinkState === null || getSmartlinkState === void 0 ? void 0 : getSmartlinkState()[url || '']; const onClick = getCardClickHandler(eventHandlers, url); const cardProps = { url, data, onClick, container: portal }; const { hideHoverPreview, actionOptions, ssr, getResolvingPlaceholder } = smartLinks || {}; const resolvingPlaceholder = url && getResolvingPlaceholder ? getResolvingPlaceholder(url) : undefined; const analyticsData = { attributes: { location: 'renderer' }, // Below is added for the future implementation of Linking Platform namespaced analytic context location: 'renderer' }; const inlineAnnotationProps = useInlineAnnotationProps(props); const CompetitorPrompt = smartLinks === null || smartLinks === void 0 ? void 0 : smartLinks.CompetitorPrompt; const CompetitorPromptComponent = CompetitorPrompt && url ? jsx(CompetitorPrompt, { sourceUrl: url, linkType: "inline" }) : null; const onError = ({ err }) => { if (err) { throw err; } }; useEffect(() => { if (expValEquals('platform_editor_smartlink_local_cache', 'isEnabled', true) && url) { // Refresh cache in the background provider === null || provider === void 0 ? void 0 : provider.then(providerInstance => { var _refreshCache, _ref; (_refreshCache = (_ref = providerInstance).refreshCache) === null || _refreshCache === void 0 ? void 0 : _refreshCache.call(_ref, { // It's ok to cast any resourceUrl to inlineCard here, because only URL is important for the request. type: 'inlineCard', attrs: { url } }); }); } }, [provider, url]); const MaybeOverlay = cardContext !== null && cardContext !== void 0 && cardContext.value ? OverlayWithCardContext : HoverLinkOverlayNoop; if ((ssr || cardState && expValEquals('platform_editor_smartlink_local_cache', 'isEnabled', true)) && url && !editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true })) { if ( // eslint-disable-next-line @atlaskit/platform/no-invalid-feature-flag-usage fg('editor_inline_comments_on_inline_nodes')) { return jsx(SmartLinkDraggable, { url: url, appearance: SMART_LINK_APPEARANCE.INLINE, source: SMART_LINK_DRAG_TYPES.RENDERER }, jsx("span", _extends({ "data-inline-card": true // eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- Ignored via go/ees017 (to be fixed) , "data-card-data": data ? JSON.stringify(data) : undefined, "data-card-url": url // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, inlineAnnotationProps), jsx(AnalyticsContext, { data: analyticsData }, wrapWithSuspense(jsx(CardSSR, { appearance: "inline", url: url, showHoverPreview: !hideHoverPreview, actionOptions: actionOptions, onClick: onClick, resolvingPlaceholder: resolvingPlaceholder }))))); } return jsx(SmartLinkDraggable, { url: url, appearance: SMART_LINK_APPEARANCE.INLINE, source: SMART_LINK_DRAG_TYPES.RENDERER }, jsx(AnalyticsContext, { data: analyticsData }, wrapWithSuspense(jsx(CardSSR, { appearance: "inline", url: url, showHoverPreview: !hideHoverPreview, actionOptions: actionOptions, onClick: onClick, resolvingPlaceholder: resolvingPlaceholder })), CompetitorPromptComponent)); } else if ((ssr || cardState && expValEquals('platform_editor_smartlink_local_cache', 'isEnabled', true)) && url && editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true })) { if ( // eslint-disable-next-line @atlaskit/platform/no-invalid-feature-flag-usage fg('editor_inline_comments_on_inline_nodes')) { return jsx(SmartLinkDraggable, { url: url, appearance: SMART_LINK_APPEARANCE.INLINE, source: SMART_LINK_DRAG_TYPES.RENDERER }, jsx("span", { "data-inline-card": true // eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- Ignored via go/ees017 (to be fixed) , "data-card-data": data ? JSON.stringify(data) : undefined, "data-card-url": url, "data-renderer-mark": inlineAnnotationProps['data-renderer-mark'], "data-annotation-draft-mark": inlineAnnotationProps['data-annotation-draft-mark'], "data-annotation-inline-node": inlineAnnotationProps['data-annotation-inline-node'], "data-renderer-start-pos": inlineAnnotationProps['data-renderer-start-pos'], "data-annotation-mark": inlineAnnotationProps['data-annotation-mark'] }, jsx(AnalyticsContext, { data: analyticsData }, jsx(MaybeOverlay, { url: url || '', rendererAppearance: rendererAppearance, isResolvedViewRendered: isResolvedViewRendered, fireAnalyticsEvent: fireAnalyticsEvent }, wrapWithSuspense(jsx(CardSSR, { appearance: "inline", url: url, showHoverPreview: !hideHoverPreview, actionOptions: actionOptions, onClick: onClick, resolvingPlaceholder: resolvingPlaceholder // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onResolve: data => { if (!data.url || !data.title) { return; } props.smartCardStorage.set(data.url, data.title); if (data.title) { setIsResolvedViewRendered(true); } }, onError: onError, disablePreviewPanel: true })))))); } return jsx(SmartLinkDraggable, { url: url, appearance: SMART_LINK_APPEARANCE.INLINE, source: SMART_LINK_DRAG_TYPES.RENDERER }, jsx(AnalyticsContext, { data: analyticsData }, jsx(MaybeOverlay, { url: url || '', rendererAppearance: rendererAppearance, isResolvedViewRendered: isResolvedViewRendered, fireAnalyticsEvent: fireAnalyticsEvent }, wrapWithSuspense(jsx(CardSSR, { appearance: "inline", url: url, showHoverPreview: !hideHoverPreview, actionOptions: actionOptions, onClick: onClick, resolvingPlaceholder: resolvingPlaceholder // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onResolve: data => { if (!data.url || !data.title) { return; } props.smartCardStorage.set(data.url, data.title); if (data.title) { setIsResolvedViewRendered(true); } }, onError: onError, disablePreviewPanel: true }))), CompetitorPromptComponent)); } return jsx(SmartLinkDraggable, { url: url || '', appearance: SMART_LINK_APPEARANCE.INLINE, source: SMART_LINK_DRAG_TYPES.RENDERER }, jsx(AnalyticsContext, { data: analyticsData }, jsx("span", _extends({ "data-inline-card": true // eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- Ignored via go/ees017 (to be fixed) , "data-card-data": data ? JSON.stringify(data) : undefined, "data-card-url": url // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, inlineAnnotationProps), jsx(CardErrorBoundary, _extends({ unsupportedComponent: UnsupportedInline // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, cardProps, { onSetLinkTarget: onSetLinkTarget }), jsx(MaybeOverlay, { url: url || '', rendererAppearance: rendererAppearance, isResolvedViewRendered: isResolvedViewRendered, fireAnalyticsEvent: fireAnalyticsEvent }, wrapWithSuspense(jsx(Card, _extends({ appearance: "inline", showHoverPreview: !hideHoverPreview, actionOptions: actionOptions, resolvingPlaceholder: resolvingPlaceholder // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, cardProps, { // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) onResolve: data => { if (!data.url || !data.title) { return; } props.smartCardStorage.set(data.url, data.title); if (data.title) { setIsResolvedViewRendered(true); } }, onError: onError, disablePreviewPanel: editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true }) })))), CompetitorPromptComponent)))); }; const _default_1 = withSmartCardStorage(InlineCard); export default _default_1;