note_awards_list.vue 6.52 KB
Newer Older
1
<script>
Fatih Acet's avatar
Fatih Acet committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { mapActions, mapGetters } from 'vuex';
import emojiSmiling from 'icons/_emoji_slightly_smiling_face.svg';
import emojiSmile from 'icons/_emoji_smile.svg';
import emojiSmiley from 'icons/_emoji_smiley.svg';
import Flash from '../../flash';
import { glEmojiTag } from '../../emoji';
import tooltip from '../../vue_shared/directives/tooltip';

export default {
  directives: {
    tooltip,
  },
  props: {
    awards: {
      type: Array,
      required: true,
Filipa Lacerda's avatar
Filipa Lacerda committed
18
    },
Fatih Acet's avatar
Fatih Acet committed
19
20
21
    toggleAwardPath: {
      type: String,
      required: true,
22
    },
Fatih Acet's avatar
Fatih Acet committed
23
24
25
    noteAuthorId: {
      type: Number,
      required: true,
26
    },
Fatih Acet's avatar
Fatih Acet committed
27
    noteId: {
28
      type: String,
Fatih Acet's avatar
Fatih Acet committed
29
      required: true,
Filipa Lacerda's avatar
Filipa Lacerda committed
30
    },
31
32
33
34
    canAwardEmoji: {
      type: Boolean,
      required: true,
    },
Fatih Acet's avatar
Fatih Acet committed
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  },
  computed: {
    ...mapGetters(['getUserData']),
    // `this.awards` is an array with emojis but they are not grouped by emoji name. See below.
    // [ { name: foo, user: user1 }, { name: bar, user: user1 }, { name: foo, user: user2 } ]
    // This method will group emojis by their name as an Object. See below.
    // {
    //   foo: [ { name: foo, user: user1 }, { name: foo, user: user2 } ],
    //   bar: [ { name: bar, user: user1 } ]
    // }
    // We need to do this otherwise we will render the same emoji over and over again.
    groupedAwards() {
      const awards = this.awards.reduce((acc, award) => {
        if (Object.prototype.hasOwnProperty.call(acc, award.name)) {
          acc[award.name].push(award);
        } else {
          Object.assign(acc, { [award.name]: [award] });
Filipa Lacerda's avatar
Filipa Lacerda committed
52
53
        }

Fatih Acet's avatar
Fatih Acet committed
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
        return acc;
      }, {});

      const orderedAwards = {};
      const { thumbsdown, thumbsup } = awards;
      // Always show thumbsup and thumbsdown first
      if (thumbsup) {
        orderedAwards.thumbsup = thumbsup;
        delete awards.thumbsup;
      }
      if (thumbsdown) {
        orderedAwards.thumbsdown = thumbsdown;
        delete awards.thumbsdown;
      }

      return Object.assign({}, orderedAwards, awards);
    },
    isAuthoredByMe() {
      return this.noteAuthorId === this.getUserData.id;
    },
  },
  created() {
    this.emojiSmiling = emojiSmiling;
    this.emojiSmile = emojiSmile;
    this.emojiSmiley = emojiSmiley;
  },
  methods: {
    ...mapActions(['toggleAwardRequest']),
    getAwardHTML(name) {
      return glEmojiTag(name);
    },
85
    getAwardClassBindings(awardList) {
Fatih Acet's avatar
Fatih Acet committed
86
87
      return {
        active: this.hasReactionByCurrentUser(awardList),
88
        disabled: !this.canInteractWithEmoji(),
Fatih Acet's avatar
Fatih Acet committed
89
90
      };
    },
91
92
    canInteractWithEmoji() {
      return this.getUserData.id;
Fatih Acet's avatar
Fatih Acet committed
93
94
    },
    hasReactionByCurrentUser(awardList) {
95
      return awardList.filter(award => award.user.id === this.getUserData.id).length;
Fatih Acet's avatar
Fatih Acet committed
96
97
    },
    awardTitle(awardsList) {
98
      const hasReactionByCurrentUser = this.hasReactionByCurrentUser(awardsList);
Fatih Acet's avatar
Fatih Acet committed
99
100
101
102
103
      const TOOLTIP_NAME_COUNT = hasReactionByCurrentUser ? 9 : 10;
      let awardList = awardsList;

      // Filter myself from list if I am awarded.
      if (hasReactionByCurrentUser) {
104
        awardList = awardList.filter(award => award.user.id !== this.getUserData.id);
Fatih Acet's avatar
Fatih Acet committed
105
106
107
      }

      // Get only 9-10 usernames to show in tooltip text.
108
      const namesToShow = awardList.slice(0, TOOLTIP_NAME_COUNT).map(award => award.user.name);
Fatih Acet's avatar
Fatih Acet committed
109
110

      // Get the remaining list to use in `and x more` text.
111
      const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
Fatih Acet's avatar
Fatih Acet committed
112

113
      // Add myself to the beginning of the list so title will start with You.
Fatih Acet's avatar
Fatih Acet committed
114
115
116
117
118
119
120
121
      if (hasReactionByCurrentUser) {
        namesToShow.unshift('You');
      }

      let title = '';

      // We have 10+ awarded user, join them with comma and add `and x more`.
      if (remainingAwardList.length) {
122
        title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`;
Fatih Acet's avatar
Fatih Acet committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
      } else if (namesToShow.length > 1) {
        // Join all names with comma but not the last one, it will be added with and text.
        title = namesToShow.slice(0, namesToShow.length - 1).join(', ');
        // If we have more than 2 users we need an extra comma before and text.
        title += namesToShow.length > 2 ? ',' : '';
        title += ` and ${namesToShow.slice(-1)}`; // Append and text
      } else {
        // We have only 2 users so join them with and.
        title = namesToShow.join(' and ');
      }

      return title;
    },
    handleAward(awardName) {
137
      if (!this.canAwardEmoji) {
Fatih Acet's avatar
Fatih Acet committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
        return;
      }

      let parsedName;

      // 100 and 1234 emoji are a number. Callback for v-for click sends it as a string
      switch (awardName) {
        case '100':
          parsedName = 100;
          break;
        case '1234':
          parsedName = 1234;
          break;
        default:
          parsedName = awardName;
          break;
      }

      const data = {
        endpoint: this.toggleAwardPath,
        noteId: this.noteId,
        awardName: parsedName,
      };

162
      this.toggleAwardRequest(data).catch(() => Flash('Something went wrong on our end.'));
163
    },
Fatih Acet's avatar
Fatih Acet committed
164
165
  },
};
166
167
168
169
170
171
</script>

<template>
  <div class="note-awards">
    <div class="awards js-awards-block">
      <button
172
173
        v-for="(awardList, awardName, index) in groupedAwards"
        :key="index"
174
        v-tooltip
175
        :class="getAwardClassBindings(awardList)"
176
        :title="awardTitle(awardList)"
177
        class="btn award-control"
178
        data-boundary="viewport"
179
        data-placement="bottom"
180
181
        type="button"
        @click="handleAward(awardName)">
182
        <span v-html="getAwardHTML(awardName)"></span>
183
        <span class="award-control-text js-counter">
Filipa Lacerda's avatar
Filipa Lacerda committed
184
          {{ awardList.length }}
185
186
        </span>
      </button>
Fatih Acet's avatar
Fatih Acet committed
187
      <div
188
        v-if="canAwardEmoji"
Fatih Acet's avatar
Fatih Acet committed
189
        class="award-menu-holder">
190
        <button
191
          v-tooltip
192
          :class="{ 'js-user-authored': isAuthoredByMe }"
193
          class="award-control btn js-add-award"
194
          title="Add reaction"
195
          aria-label="Add reaction"
196
          data-boundary="viewport"
197
198
199
          data-placement="bottom"
          type="button">
          <span
200
201
            class="award-control-icon award-control-icon-neutral"
            v-html="emojiSmiling">
202
          </span>
203
          <span
204
205
            class="award-control-icon award-control-icon-positive"
            v-html="emojiSmiley">
206
          </span>
207
          <span
208
209
            class="award-control-icon award-control-icon-super-positive"
            v-html="emojiSmile">
210
          </span>
211
212
213
214
215
216
217
218
          <i
            aria-hidden="true"
            class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>
        </button>
      </div>
    </div>
  </div>
</template>