diff options
Diffstat (limited to 'app/assets/javascripts/user_popovers.js')
-rw-r--r-- | app/assets/javascripts/user_popovers.js | 139 |
1 files changed, 66 insertions, 73 deletions
diff --git a/app/assets/javascripts/user_popovers.js b/app/assets/javascripts/user_popovers.js index 438ae2bc1bc..a3615eab26f 100644 --- a/app/assets/javascripts/user_popovers.js +++ b/app/assets/javascripts/user_popovers.js @@ -1,6 +1,8 @@ import Vue from 'vue'; +import { debounce } from 'lodash'; import UsersCache from './lib/utils/users_cache'; import UserPopover from './vue_shared/components/user_popover/user_popover.vue'; +import { USER_POPOVER_DELAY } from './vue_shared/components/user_popover/constants'; const removeTitle = (el) => { // Removing titles so its not showing tooltips also @@ -59,87 +61,78 @@ const populateUserInfo = (user) => { ); }; -const initializedPopovers = new Map(); -let domObservedForChanges = false; +function createPopover(el, user) { + removeTitle(el); + const preloadedUserInfo = getPreloadedUserInfo(el.dataset); -const addPopoversToModifiedTree = new MutationObserver(() => { - const userLinks = document?.querySelectorAll('.js-user-link, .gfm-project_member'); + Object.assign(user, preloadedUserInfo); - if (userLinks) { - addPopovers(userLinks); /* eslint-disable-line no-use-before-define */ + if (preloadedUserInfo.userId) { + populateUserInfo(user); } -}); + const UserPopoverComponent = Vue.extend(UserPopover); + return new UserPopoverComponent({ + propsData: { + target: el, + user, + show: true, + placement: el.dataset.placement || 'top', + }, + }); +} -function observeBody() { - if (!domObservedForChanges) { - addPopoversToModifiedTree.observe(document.body, { - subtree: true, - childList: true, - }); +function launchPopover(el, mountPopover) { + if (el.user) return; - domObservedForChanges = true; - } + const emptyUser = { + location: null, + bio: null, + workInformation: null, + status: null, + isFollowed: false, + loaded: false, + }; + el.user = emptyUser; + el.addEventListener( + 'mouseleave', + ({ target }) => { + target.removeAttribute('aria-describedby'); + }, + { once: true }, + ); + const popoverInstance = createPopover(el, emptyUser); + + const { userId } = el.dataset; + + popoverInstance.$on('follow', () => { + UsersCache.updateById(userId, { is_followed: true }); + el.user.isFollowed = true; + }); + + popoverInstance.$on('unfollow', () => { + UsersCache.updateById(userId, { is_followed: false }); + el.user.isFollowed = false; + }); + + mountPopover(popoverInstance); } -export default function addPopovers(elements = document.querySelectorAll('.js-user-link')) { - const userLinks = Array.from(elements); - const UserPopoverComponent = Vue.extend(UserPopover); +const userLinkSelector = 'a.js-user-link, a.gfm-project_member'; - observeBody(); +const getUserLinkNode = (node) => node.closest(userLinkSelector); - return userLinks - .filter(({ dataset }) => dataset.user || dataset.userId) - .map((el) => { - if (initializedPopovers.has(el)) { - return initializedPopovers.get(el); - } +const lazyLaunchPopover = debounce((mountPopover, event) => { + const userLink = getUserLinkNode(event.target); + if (userLink) { + launchPopover(userLink, mountPopover); + } +}, USER_POPOVER_DELAY); - const user = { - location: null, - bio: null, - workInformation: null, - status: null, - isFollowed: false, - loaded: false, - }; - const renderedPopover = new UserPopoverComponent({ - propsData: { - target: el, - user, - placement: el.dataset.placement || 'top', - }, - }); - - const { userId } = el.dataset; - - renderedPopover.$on('follow', () => { - UsersCache.updateById(userId, { is_followed: true }); - user.isFollowed = true; - }); - - renderedPopover.$on('unfollow', () => { - UsersCache.updateById(userId, { is_followed: false }); - user.isFollowed = false; - }); - - initializedPopovers.set(el, renderedPopover); - - renderedPopover.$mount(); - - el.addEventListener('mouseenter', ({ target }) => { - removeTitle(target); - const preloadedUserInfo = getPreloadedUserInfo(target.dataset); - - Object.assign(user, preloadedUserInfo); - - if (preloadedUserInfo.userId) { - populateUserInfo(user); - } - }); - el.addEventListener('mouseleave', ({ target }) => { - target.removeAttribute('aria-describedby'); - }); - - return renderedPopover; - }); +let hasAddedLazyPopovers = false; + +export default function addPopovers(mountPopover = (instance) => instance.$mount()) { + if (!hasAddedLazyPopovers) { + document.addEventListener('mouseover', (event) => lazyLaunchPopover(mountPopover, event)); + hasAddedLazyPopovers = true; + } } |