summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/user_popovers.js
blob: 948f4d5e631cf78d492abca18847dc65ee77107c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import Vue from 'vue';

import UsersCache from './lib/utils/users_cache';
import UserPopover from './vue_shared/components/user_popover/user_popover.vue';

let renderedPopover;
let renderFn;

const handleUserPopoverMouseOut = event => {
  const { target } = event;
  target.removeEventListener('mouseleave', handleUserPopoverMouseOut);

  if (renderFn) {
    clearTimeout(renderFn);
  }
  if (renderedPopover) {
    renderedPopover.$destroy();
    renderedPopover = null;
  }
};

/**
 * Adds a UserPopover component to the body, hands over as much data as the target element has in data attributes.
 * loads based on data-user-id more data about a user from the API and sets it on the popover
 */
const handleUserPopoverMouseOver = event => {
  const { target } = event;
  // Add listener to actually remove it again
  target.addEventListener('mouseleave', handleUserPopoverMouseOut);

  renderFn = setTimeout(() => {
    // Helps us to use current markdown setup without maybe breaking or duplicating for now
    if (target.dataset.user) {
      target.dataset.userId = target.dataset.user;
      // Removing titles so its not showing tooltips also
      target.dataset.originalTitle = '';
      target.setAttribute('title', '');
    }

    const { userId, username, name, avatarUrl } = target.dataset;
    const user = {
      userId,
      username,
      name,
      avatarUrl,
      location: null,
      bio: null,
      organization: null,
      status: null,
      loaded: false,
    };
    if (userId || username) {
      const UserPopoverComponent = Vue.extend(UserPopover);
      renderedPopover = new UserPopoverComponent({
        propsData: {
          target,
          user,
        },
      });

      renderedPopover.$mount();

      UsersCache.retrieveById(userId)
        .then(userData => {
          if (!userData) {
            return;
          }

          Object.assign(user, {
            avatarUrl: userData.avatar_url,
            username: userData.username,
            name: userData.name,
            location: userData.location,
            bio: userData.bio,
            organization: userData.organization,
            loaded: true,
          });

          UsersCache.retrieveStatusById(userId)
            .then(status => {
              if (!status) {
                return;
              }

              Object.assign(user, {
                status,
              });
            })
            .catch(() => {
              throw new Error(`User status for "${userId}" could not be retrieved!`);
            });
        })
        .catch(() => {
          renderedPopover.$destroy();
          renderedPopover = null;
        });
    }
  }, 200); // 200ms delay so not every mouseover triggers Popover + API Call
};

export default elements => {
  const userLinks = elements || [...document.querySelectorAll('.js-user-link')];

  userLinks.forEach(el => {
    el.addEventListener('mouseenter', handleUserPopoverMouseOver);
  });
};