in dotcom-rendering/src/components/LiveBlogEpic.importable.tsx [126:279]
epicViewLog: getEpicViewLog(storage.local),
weeklyArticleHistory: articleCounts?.weeklyArticleHistory,
hasOptedOutOfArticleCount,
url: window.location.origin + window.location.pathname,
isSignedIn,
pageId,
},
};
};
/**
* Render
*
* Dynamically imports and renders a given module
*
* @param props
* @param name string - The name of the component
* @param props.props object - The props of the component
* @returns The resulting react component
*/
const Render = ({ name, props }: { name: string; props: EpicProps }) => {
const { Epic } = useEpic({ name });
if (isUndefined(Epic)) return null;
log('dotcom', 'LiveBlogEpic has the Epic');
return (
<aside
css={css`
margin-bottom: ${space[3]}px;
`}
>
{}
<Epic {...props} />
</aside>
);
};
/**
* Using the payload, make a fetch request to contributions service
*/
const Fetch = ({
payload,
contributionsServiceUrl,
ophanPageViewId,
renderingTarget,
pageUrl,
}: {
payload: EpicPayload;
contributionsServiceUrl: string;
ophanPageViewId: string;
renderingTarget: RenderingTarget;
pageUrl: string;
}) => {
const response = useSDCLiveblogEpic(contributionsServiceUrl, payload);
// If we didn't get a module in response (or we're still waiting) do nothing. If
// no epic should be shown the response is equal to {}, hence the Object.keys
if (!response?.data || Object.keys(response).length === 0) {
return null;
}
log('dotcom', 'LiveBlogEpic has a module');
const { props } = response.data.module;
const tracking: Tracking = {
...props.tracking,
ophanPageId: ophanPageViewId,
platformId: 'GUARDIAN_WEB',
referrerUrl: pageUrl,
};
// Add submitComponentEvent function to props to enable Ophan tracking in the component
const enrichedProps: EpicProps = {
...props,
tracking,
submitComponentEvent: (componentEvent: OphanComponentEvent) =>
submitComponentEvent(componentEvent, renderingTarget),
};
// Take any returned module and render it
return <Render name={response.data.module.name} props={enrichedProps} />;
};
function insertAfter(referenceNode: HTMLElement, newNode: Element) {
referenceNode.parentNode?.insertBefore(newNode, referenceNode.nextSibling);
}
/**
* LiveBlogEpic is the support epic which appears in-between blocks in blogs
*
* There are three main steps to create this epic:
*
* 1. usePayload - Build the payload. The config object we send to contributions
* 2. Fetch - POST the payload to the contributions endpoint
* 3. Render - Take the props and name data we got in response to our fetch and dynamically import
* and render the Epic component using it
*
* ## Why does this need to be an Island?
*
* All behaviour is client-side.
*
* ---
*
* (No visual story exist)
*/
export const LiveBlogEpic = ({
sectionId,
shouldHideReaderRevenue,
isPaidContent,
tags,
contributionsServiceUrl,
pageId,
keywordIds,
renderingTarget,
}: Props) => {
log('dotcom', 'LiveBlogEpic started');
const ophanPageViewId = usePageViewId(renderingTarget);
const [pageUrl, setPageUrl] = useState<string | undefined>();
useEffect(() => {
setPageUrl(window.location.origin + window.location.pathname);
}, []);
// First construct the payload
const payload = usePayload({
shouldHideReaderRevenue,
sectionId,
isPaidContent,
tags,
pageId,
keywordIds,
});
if (!ophanPageViewId || !payload || !pageUrl) return null;
/**
* Here we decide where to insert the epic.
*
* If the url contains a permalink then we
* want to insert it immediately after that block to prevent any CLS issues.
*
* Otherwise, we choose a random position near the top of the blog
*/
const epicPlaceholder = document.createElement('article');
if (window.location.href.includes('#block-')) {
// Because we're using a permalink there's a possibility the epic will render in
// view. To prevent confusing layout shift we initially hide the message so that
// we can reveal (animate in) it once it has been rendered
epicPlaceholder.classList.add('pending');
const blockId = window.location.hash.slice(1);
const blockLinkedTo = document.getElementById(blockId);
if (blockLinkedTo) {