* create new pack, handle user follow buttons, use pack on article page, remove initializeUserFollowButts * init all follow button types, add pack to tag index and podcast episode pages * add pack to all relevant pages, listen for newly inserted follow buttons * change to searchParams to remove follow button initializer calls * fix bug with tag page, add pack to notifications page * fix issue with follow back inner text * update cypress specs * run the followbuttons code on sponsors page * remove extra foreach * add test for follow from article sidebar * add test for follow and unfollow tag * add test for the tag index page * add spec for organisation profile follow * add tests for follow buttons in search results * add tests for notification follows * commit missed file - woops * show login modal if user is logged out when they click * add cypress tests for logged out state * change tag button initialization * remove data-button-initialized * init follow buttons from base pack * handle the case where multiple follow buttons exist on a page for the same user * account for instantclick and userdata not being defined, lower coverage for jest * use getInstantClick * fix issue with set initialisation * only listen for mutations in areas we know follow buttons may be added dynamically * small refactors
143 lines
4.2 KiB
JavaScript
143 lines
4.2 KiB
JavaScript
import {
|
|
initializeMobileMenu,
|
|
setCurrentPageIconLink,
|
|
getInstantClick,
|
|
initializeMemberMenu,
|
|
} from '../topNavigation/utilities';
|
|
|
|
// Unique ID applied to modals created using window.Forem.showModal
|
|
const WINDOW_MODAL_ID = 'window-modal';
|
|
|
|
// Namespace for functions which need to be accessed in plain JS initializers
|
|
window.Forem = {
|
|
preactImport: undefined,
|
|
getPreactImport() {
|
|
if (!this.preactImport) {
|
|
this.preactImport = import('preact');
|
|
}
|
|
return this.preactImport;
|
|
},
|
|
mentionAutoCompleteImports: undefined,
|
|
getMentionAutoCompleteImports() {
|
|
if (!this.mentionAutoCompleteImports) {
|
|
this.mentionAutoCompleteImports = [
|
|
import('@crayons/MentionAutocompleteTextArea'),
|
|
import('@utilities/search'),
|
|
this.getPreactImport(),
|
|
];
|
|
}
|
|
|
|
// We're still returning Promises, but if the they have already been imported
|
|
// they will now be fulfilled instead of pending, i.e. a network request is no longer made.
|
|
return Promise.all(this.mentionAutoCompleteImports);
|
|
},
|
|
initializeMentionAutocompleteTextArea: async (originalTextArea) => {
|
|
const parentContainer = originalTextArea.parentElement;
|
|
|
|
const alreadyInitialized = parentContainer.id === 'combobox-container';
|
|
if (alreadyInitialized) {
|
|
return;
|
|
}
|
|
|
|
const [{ MentionAutocompleteTextArea }, { fetchSearch }, { render, h }] =
|
|
await window.Forem.getMentionAutoCompleteImports();
|
|
|
|
render(
|
|
<MentionAutocompleteTextArea
|
|
replaceElement={originalTextArea}
|
|
fetchSuggestions={(username) => fetchSearch('usernames', { username })}
|
|
/>,
|
|
parentContainer,
|
|
originalTextArea,
|
|
);
|
|
},
|
|
modalImports: undefined,
|
|
getModalImports() {
|
|
if (!this.modalImports) {
|
|
this.modalImports = [import('@crayons/Modal'), this.getPreactImport()];
|
|
}
|
|
return Promise.all(this.modalImports);
|
|
},
|
|
showModal: async ({
|
|
title,
|
|
contentSelector,
|
|
overlay = false,
|
|
size = 's',
|
|
}) => {
|
|
const [{ Modal }, { render, h }] = await window.Forem.getModalImports();
|
|
|
|
// Guard against two modals being opened at once
|
|
let currentModalContainer = document.getElementById(WINDOW_MODAL_ID);
|
|
if (currentModalContainer) {
|
|
render(null, currentModalContainer);
|
|
} else {
|
|
currentModalContainer = document.createElement('div');
|
|
currentModalContainer.setAttribute('id', WINDOW_MODAL_ID);
|
|
document.body.appendChild(currentModalContainer);
|
|
}
|
|
|
|
render(
|
|
<Modal
|
|
overlay={overlay}
|
|
title={title}
|
|
onClose={() => {
|
|
render(null, currentModalContainer);
|
|
}}
|
|
size={size}
|
|
focusTrapSelector={`#${WINDOW_MODAL_ID}`}
|
|
>
|
|
<div
|
|
// eslint-disable-next-line react/no-danger
|
|
dangerouslySetInnerHTML={{
|
|
__html: document.querySelector(contentSelector).innerHTML,
|
|
}}
|
|
/>
|
|
</Modal>,
|
|
currentModalContainer,
|
|
);
|
|
},
|
|
closeModal: async () => {
|
|
const currentModalContainer = document.getElementById(WINDOW_MODAL_ID);
|
|
if (currentModalContainer) {
|
|
const { render } = await window.Forem.getPreactImport();
|
|
render(null, currentModalContainer);
|
|
}
|
|
},
|
|
};
|
|
|
|
function getPageEntries() {
|
|
return Object.entries({
|
|
'notifications-index': document.getElementById('notifications-link'),
|
|
'chat_channels-index': document.getElementById('connect-link'),
|
|
'moderations-index': document.getElementById('moderation-link'),
|
|
'stories-search': document.getElementById('search-link'),
|
|
});
|
|
}
|
|
|
|
function initializeNav() {
|
|
const { currentPage } = document.getElementById('page-content').dataset;
|
|
const menuTriggers = [
|
|
...document.querySelectorAll(
|
|
'.js-hamburger-trigger, .hamburger a:not(.js-nav-more-trigger)',
|
|
),
|
|
];
|
|
const moreMenus = [...document.getElementsByClassName('js-nav-more-trigger')];
|
|
|
|
setCurrentPageIconLink(currentPage, getPageEntries());
|
|
initializeMobileMenu(menuTriggers, moreMenus);
|
|
}
|
|
|
|
const memberMenu = document.getElementById('crayons-header__menu');
|
|
const menuNavButton = document.getElementById('member-menu-button');
|
|
|
|
if (memberMenu) {
|
|
initializeMemberMenu(memberMenu, menuNavButton);
|
|
}
|
|
|
|
getInstantClick().then((spa) => {
|
|
spa.on('change', () => {
|
|
initializeNav();
|
|
});
|
|
});
|
|
|
|
initializeNav();
|