docbrown/app/javascript/packs/articlePage.jsx
Daniel Uber c5a90173bd
handle getElementById failures in articlePage (#16743)
* when there is no div for it, don't render CommentSubscription

* Retrigger CI

* Retrigger CI

* don't write an error to the comment div if its gone

This fixes an issue seen in tests, where the page transitioned while
we were running the articlePage async handler, so the article-body
element was gone (can't access dataset of null), and the error handler
catch block tried to access the innerHTML of another getElementById.

* Use existing found element `root` when setting the error message

No need to find a new comment-subscription div to write the error to,
we already named the one we're interested in at the beginning of the function
2022-03-02 09:56:53 -06:00

138 lines
4.2 KiB
JavaScript

import { h, render } from 'preact';
import { Snackbar, addSnackbarItem } from '../Snackbar';
import { addFullScreenModeControl } from '../utilities/codeFullscreenModeSwitcher';
import { initializeDropdown } from '../utilities/dropdownUtils';
import { embedGists } from '../utilities/gist';
import { initializeUserSubscriptionLiquidTagContent } from '../liquidTags/userSubscriptionLiquidTag';
/* global Runtime */
const animatedImages = document.querySelectorAll('[data-animated="true"]');
if (animatedImages.length > 0) {
import('@utilities/animatedImageUtils').then(
({ initializePausableAnimatedImages }) => {
initializePausableAnimatedImages(animatedImages);
},
);
}
const fullscreenActionElements = document.getElementsByClassName(
'js-fullscreen-code-action',
);
if (fullscreenActionElements) {
addFullScreenModeControl(fullscreenActionElements);
}
// The Snackbar for the article page
const snackZone = document.getElementById('snack-zone');
if (snackZone) {
render(<Snackbar lifespan={3} />, snackZone);
}
// eslint-disable-next-line no-restricted-globals
top.addSnackbarItem = addSnackbarItem;
// Dropdown accessibility
function hideCopyLinkAnnouncerIfVisible() {
document.getElementById('article-copy-link-announcer').hidden = true;
}
// Initialize the share options
const shareDropdownButton = document.getElementById('article-show-more-button');
if (shareDropdownButton.dataset.initialized !== 'true') {
if (Runtime.isNativeAndroid('shareText')) {
// Android native apps have enhanced sharing capabilities for Articles and don't use our standard dropdown
shareDropdownButton.addEventListener('click', () =>
AndroidBridge.shareText(location.href),
);
} else {
const { closeDropdown } = initializeDropdown({
triggerElementId: 'article-show-more-button',
dropdownContentId: 'article-show-more-dropdown',
onClose: hideCopyLinkAnnouncerIfVisible,
});
// We want to close the dropdown on link select (since they open in a new tab)
document
.querySelectorAll('#article-show-more-dropdown [href]')
.forEach((link) => {
link.addEventListener('click', (event) => {
closeDropdown(event);
});
});
}
shareDropdownButton.dataset.initialized = 'true';
}
// Initialize the copy to clipboard functionality
function showAnnouncer() {
document.getElementById('article-copy-link-announcer').hidden = false;
}
function copyArticleLink() {
const postUrlValue = document
.getElementById('copy-post-url-button')
.getAttribute('data-postUrl');
Runtime.copyToClipboard(postUrlValue).then(() => {
showAnnouncer();
});
}
document
.getElementById('copy-post-url-button')
?.addEventListener('click', copyArticleLink);
// Comment Subscription
getCsrfToken().then(async () => {
const { user = null, userStatus } = document.body.dataset;
const root = document.getElementById('comment-subscription');
const isLoggedIn = userStatus === 'logged-in';
if (!root) {
return;
}
try {
const {
getCommentSubscriptionStatus,
setCommentSubscriptionStatus,
CommentSubscription,
} = await import('../CommentSubscription');
const { articleId } = document.getElementById('article-body').dataset;
let subscriptionType = 'not_subscribed';
if (isLoggedIn && user !== null) {
({ config: subscriptionType } = await getCommentSubscriptionStatus(
articleId,
));
}
const subscriptionRequestHandler = async (type) => {
const message = await setCommentSubscriptionStatus(articleId, type);
addSnackbarItem({ message, addCloseButton: true });
};
render(
<CommentSubscription
subscriptionType={subscriptionType}
positionType="static"
onSubscribe={subscriptionRequestHandler}
onUnsubscribe={subscriptionRequestHandler}
isLoggedIn={isLoggedIn}
/>,
root,
);
} catch (e) {
root.innerHTML =
'<p className="color-accent-danger">Unable to load Comment Subscription component.<br />Try refreshing the page.</p>';
}
});
const targetNode = document.querySelector('#comments');
targetNode && embedGists(targetNode);
initializeUserSubscriptionLiquidTagContent();