jump_to_discussion.js 6.07 KB
Newer Older
1
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, one-var, space-before-function-paren, no-lonely-if, no-continue, brace-style, max-len, quotes */
2 3
/* global DiscussionMixins */
/* global CommentsStore */
4 5

import Vue from 'vue';
6

7 8 9 10 11 12 13 14 15 16 17 18 19 20
const JumpToDiscussion = Vue.extend({
  mixins: [DiscussionMixins],
  props: {
    discussionId: String
  },
  data: function () {
    return {
      discussions: CommentsStore.state,
      discussion: {},
    };
  },
  computed: {
    allResolved: function () {
      return this.unresolvedDiscussionCount === 0;
21
    },
22 23 24 25
    showButton: function () {
      if (this.discussionId) {
        if (this.unresolvedDiscussionCount > 1) {
          return true;
26
        } else {
27
          return this.discussionId !== this.lastResolvedId;
28
        }
29 30
      } else {
        return this.unresolvedDiscussionCount >= 1;
31 32
      }
    },
33 34 35 36
    lastResolvedId: function () {
      let lastId;
      for (const discussionId in this.discussions) {
        const discussion = this.discussions[discussionId];
37

38 39
        if (!discussion.isResolved()) {
          lastId = discussion.id;
40
        }
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
      }
      return lastId;
    }
  },
  methods: {
    jumpToNextUnresolvedDiscussion: function () {
      let discussionsSelector;
      let discussionIdsInScope;
      let firstUnresolvedDiscussionId;
      let nextUnresolvedDiscussionId;
      let activeTab = window.mrTabs.currentAction;
      let hasDiscussionsToJumpTo = true;
      let jumpToFirstDiscussion = !this.discussionId;

      const discussionIdsForElements = function(elements) {
        return elements.map(function() {
          return $(this).attr('data-discussion-id');
        }).toArray();
      };
60

61
      const discussions = this.discussions;
62

63 64 65 66 67
      if (activeTab === 'diffs') {
        discussionsSelector = '.diffs .notes[data-discussion-id]';
        discussionIdsInScope = discussionIdsForElements($(discussionsSelector));

        let unresolvedDiscussionCount = 0;
68

69
        for (let i = 0; i < discussionIdsInScope.length; i += 1) {
Connor Shea's avatar
Connor Shea committed
70
          const discussionId = discussionIdsInScope[i];
71
          const discussion = discussions[discussionId];
72 73 74 75
          if (discussion && !discussion.isResolved()) {
            unresolvedDiscussionCount += 1;
          }
        }
76

77 78 79 80 81 82 83 84 85 86 87
        if (this.discussionId && !this.discussion.isResolved()) {
          // If this is the last unresolved discussion on the diffs tab,
          // there are no discussions to jump to.
          if (unresolvedDiscussionCount === 1) {
            hasDiscussionsToJumpTo = false;
          }
        } else {
          // If there are no unresolved discussions on the diffs tab at all,
          // there are no discussions to jump to.
          if (unresolvedDiscussionCount === 0) {
            hasDiscussionsToJumpTo = false;
88
          }
89 90 91 92 93 94
        }
      } else if (activeTab !== 'notes') {
        // If we are on the commits or builds tabs,
        // there are no discussions to jump to.
        hasDiscussionsToJumpTo = false;
      }
95

96 97 98 99 100 101 102
      if (!hasDiscussionsToJumpTo) {
        // If there are no discussions to jump to on the current page,
        // switch to the notes tab and jump to the first disucssion there.
        window.mrTabs.activateTab('notes');
        activeTab = 'notes';
        jumpToFirstDiscussion = true;
      }
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
      if (activeTab === 'notes') {
        discussionsSelector = '.discussion[data-discussion-id]';
        discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
      }

      let currentDiscussionFound = false;
      for (let i = 0; i < discussionIdsInScope.length; i += 1) {
        const discussionId = discussionIdsInScope[i];
        const discussion = discussions[discussionId];

        if (!discussion) {
          // Discussions for comments on commits in this MR don't have a resolved status.
          continue;
        }

        if (!firstUnresolvedDiscussionId && !discussion.isResolved()) {
          firstUnresolvedDiscussionId = discussionId;

          if (jumpToFirstDiscussion) {
            break;
124
          }
125
        }
126

127 128 129 130 131
        if (!jumpToFirstDiscussion) {
          if (currentDiscussionFound) {
            if (!discussion.isResolved()) {
              nextUnresolvedDiscussionId = discussionId;
              break;
132
            }
133 134
            else {
              continue;
135 136
            }
          }
137 138 139 140

          if (discussionId === this.discussionId) {
            currentDiscussionFound = true;
          }
141
        }
142
      }
143

144
      nextUnresolvedDiscussionId = nextUnresolvedDiscussionId || firstUnresolvedDiscussionId;
145

146 147 148
      if (!nextUnresolvedDiscussionId) {
        return;
      }
149

150
      let $target = $(`${discussionsSelector}[data-discussion-id="${nextUnresolvedDiscussionId}"]`);
151

152 153
      if (activeTab === 'notes') {
        $target = $target.closest('.note-discussion');
Connor Shea's avatar
Connor Shea committed
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
        // If the next discussion is closed, toggle it open.
        if ($target.find('.js-toggle-content').is(':hidden')) {
          $target.find('.js-toggle-button i').trigger('click');
        }
      } else if (activeTab === 'diffs') {
        // Resolved discussions are hidden in the diffs tab by default.
        // If they are marked unresolved on the notes tab, they will still be hidden on the diffs tab.
        // When jumping between unresolved discussions on the diffs tab, we show them.
        $target.closest(".content").show();

        $target = $target.closest("tr.notes_holder");
        $target.show();

        // If we are on the diffs tab, we don't scroll to the discussion itself, but to
        // 4 diff lines above it: the line the discussion was in response to + 3 context
        let prevEl;
        for (let i = 0; i < 4; i += 1) {
          prevEl = $target.prev();

          // If the discussion doesn't have 4 lines above it, we'll have to do with fewer.
          if (!prevEl.hasClass("line_holder")) {
            break;
Connor Shea's avatar
Connor Shea committed
177
          }
178

179
          $target = prevEl;
180 181
        }
      }
182

183 184 185 186 187 188 189 190 191 192 193
      $.scrollTo($target, {
        offset: 0
      });
    }
  },
  created() {
    this.discussion = this.discussions[this.discussionId];
  },
});

Vue.component('jump-to-discussion', JumpToDiscussion);