* change readonly input to p tag * add pointer cursor and focus on copy post url option * add Enter keypress eventListener to copy post url option * add test for enter press event, refractor some test to click on copy post url option instead of just the image * make copy post url font bold * add scss class sidebar for dropdown && fix sidebar dropdown reaching under topheader in large screen resolution * resolve conflict with main * Update _actions.html.erb * changin markup to improve accessibility * make variable name appropriate, switch cypress to imitate user interaction * remove dump file, fix test to use alias
181 lines
5.7 KiB
JavaScript
181 lines
5.7 KiB
JavaScript
import { h, render } from 'preact';
|
|
import ahoy from 'ahoy.js';
|
|
import { Snackbar, addSnackbarItem } from '../Snackbar';
|
|
import { addFullScreenModeControl } from '../utilities/codeFullscreenModeSwitcher';
|
|
import { embedGists } from '../utilities/gist';
|
|
import { initializeDropdown } from '@utilities/dropdownUtils';
|
|
|
|
/* global Runtime */
|
|
|
|
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);
|
|
|
|
// Temporary Ahoy Stats for usage reports
|
|
ahoy.track('Post Dropdown', { option: event.target.text.trim() });
|
|
});
|
|
});
|
|
}
|
|
|
|
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';
|
|
|
|
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) {
|
|
document.getElementById('comment-subscription').innerHTML =
|
|
'<p className="color-accent-danger">Unable to load Comment Subscription component.<br />Try refreshing the page.</p>';
|
|
}
|
|
});
|
|
|
|
// Pin/Unpin article
|
|
// these element are added by initializeBaseUserData.js:addRelevantButtonsToArticle
|
|
const toggleArticlePin = async (button) => {
|
|
const isPinButton = button.id === 'js-pin-article';
|
|
const { articleId, path } = button.dataset;
|
|
const method = isPinButton ? 'PUT' : 'DELETE';
|
|
const body = method === 'PUT' ? JSON.stringify({ id: articleId }) : null;
|
|
|
|
const response = await fetch(path, {
|
|
method,
|
|
body,
|
|
headers: {
|
|
Accept: 'application/json',
|
|
'X-CSRF-Token': window.csrfToken,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
credentials: 'same-origin',
|
|
});
|
|
|
|
// response could potentially fail if the article is draft but we don't show
|
|
// the buttons in those cases, so I think there's no need to handle that scenario client side
|
|
if (response.ok) {
|
|
// replace id and label
|
|
button.id = isPinButton ? 'js-unpin-article' : 'js-pin-article';
|
|
button.innerHTML = `${isPinButton ? 'Unpin' : 'Pin'} Post`;
|
|
|
|
const message = isPinButton
|
|
? 'The post has been succesfully pinned'
|
|
: 'The post has been succesfully unpinned';
|
|
addSnackbarItem({ message });
|
|
}
|
|
};
|
|
|
|
const actionsContainer = document.getElementById('action-space');
|
|
const pinTargets = ['js-pin-article', 'js-unpin-article'];
|
|
actionsContainer.addEventListener('click', async (event) => {
|
|
if (pinTargets.includes(event.target.id)) {
|
|
toggleArticlePin(event.target);
|
|
}
|
|
});
|
|
|
|
// Initialize the profile preview functionality
|
|
const profilePreviewTrigger = document.getElementById(
|
|
'profile-preview-trigger',
|
|
);
|
|
if (profilePreviewTrigger?.dataset.initialized !== 'true') {
|
|
initializeDropdown({
|
|
triggerElementId: 'profile-preview-trigger',
|
|
dropdownContentId: 'profile-preview-content',
|
|
});
|
|
|
|
profilePreviewTrigger.dataset.initialized = 'true';
|
|
}
|
|
|
|
const targetNode = document.querySelector('#comments');
|
|
targetNode && embedGists(targetNode);
|