docbrown/app/javascript/packs/commentDropdowns.js
VISHAL DEEPAK 1345334b33
Added a way to sort comments on an article (#16686)
* Added a way to sort comments on an article

* Sorting of comments now uses crayons-dropwn. Some minor fixes as per comments on PR

* Add and fix test cases for sorting comments functionality

* Changes in code for sort comments.  Code  is more aligned with forem's conventions

* Added cyperss tests for sort comments on an article. Cleaned up code to better follow forem conventions

* Get fresh handle of triggerButton everytime clickOustideListener is clicked. Fix Cypress test cases to reflect the earlier.
2022-04-01 09:26:18 -05:00

147 lines
4.6 KiB
JavaScript

import { addSnackbarItem } from '../Snackbar';
import {
initializeDropdown,
getDropdownRepositionListener,
} from '@utilities/dropdownUtils';
import { locale } from '@utilities/locale';
/* global Runtime */
const handleCopyPermalink = (closeDropdown) => {
return (event) => {
event.preventDefault();
const permalink = event.target.href;
Runtime.copyToClipboard(permalink).then(() => {
addSnackbarItem({ message: 'Copied to clipboard' });
});
closeDropdown();
};
};
const initializeArticlePageDropdowns = () => {
// Gather all dropdown triggers for comment options and profile previews
const dropdownTriggers = document.querySelectorAll(
'button[id^=comment-dropdown-trigger], button[id^=comment-profile-preview-trigger-], button[id^=toggle-comments-sort-dropdown]',
);
for (const dropdownTrigger of dropdownTriggers) {
if (dropdownTrigger.dataset.initialized) {
// Make sure we only initialize once
continue;
}
const isProfilePreview = dropdownTrigger.id.includes(
'comment-profile-preview-trigger',
);
const dropdownContentId = dropdownTrigger.getAttribute('aria-controls');
const dropdownElement = document.getElementById(dropdownContentId);
if (dropdownElement) {
const { closeDropdown } = initializeDropdown({
triggerElementId: dropdownTrigger.id,
dropdownContentId,
onOpen: () => {
if (isProfilePreview) {
dropdownElement?.classList.add('showing');
}
},
onClose: () => {
if (isProfilePreview) {
dropdownElement?.classList.remove('showing');
}
},
});
// Add actual link location (SEO doesn't like these "useless" links, so adding in here instead of in HTML)
const reportAbuseWrapper = dropdownElement.querySelector(
'.report-abuse-link-wrapper',
);
if (reportAbuseWrapper) {
reportAbuseWrapper.innerHTML = `<a href="${
reportAbuseWrapper.dataset.path
}" class="crayons-link crayons-link--block">${locale(
'core.report_abuse',
)}</a>`;
}
// Initialize the "Copy link" functionality
dropdownElement
.querySelector('.permalink-copybtn')
?.addEventListener('click', handleCopyPermalink(closeDropdown));
dropdownTrigger.dataset.initialized = true;
}
}
};
/**
* Function to retrieve the Profile Preview Card for newly added comments, and asynchronously replace the placeholder
*
* @param {HTMLElement} placeholderElement The <span> placeholder element to be replaced by the preview dropdown
*/
const fetchMissingProfilePreviewCard = async (placeholderElement) => {
const {
jsCommentUserId: commentUserId,
jsDropdownContentId: dropdownContentId,
} = placeholderElement.dataset;
const response = await window.fetch(
`/profile_preview_cards/${commentUserId}`,
);
const htmlContent = await response.text();
const generatedElement = document.createElement('div');
generatedElement.innerHTML = htmlContent;
const { firstElementChild: previewCard } = generatedElement;
previewCard.id = dropdownContentId;
placeholderElement.parentNode.replaceChild(previewCard, placeholderElement);
};
/**
* When a new comment is added to a discussion, the preview card dropdown must be dynamically fetched.
* This function detects if a preview card placeholder has been added in a given mutation and initiates the profile card fetch.
**/
const checkMutationForProfilePreviewCardPlaceholder = (mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
const placeholder = node.getElementsByClassName(
'preview-card-placeholder',
)[0];
if (placeholder) {
fetchMissingProfilePreviewCard(placeholder);
}
}
});
};
const observer = new MutationObserver((mutationsList) => {
mutationsList.forEach((mutation) => {
if (mutation.type === 'childList') {
checkMutationForProfilePreviewCardPlaceholder(mutation);
initializeArticlePageDropdowns();
}
});
});
observer.observe(document.getElementById('comment-trees-container'), {
childList: true,
subtree: true,
});
// Preview card dropdowns reposition on scroll
const dropdownRepositionListener = getDropdownRepositionListener();
document.addEventListener('scroll', dropdownRepositionListener);
InstantClick.on('change', () => {
observer.disconnect();
document.removeEventListener('scroll', dropdownRepositionListener);
});
window.addEventListener('beforeunload', () => {
observer.disconnect();
document.removeEventListener('scroll', dropdownRepositionListener);
});
initializeArticlePageDropdowns();