TWtablero/tablero

View on GitHub
app/js/component/data/github_issues.js

Summary

Maintainability
F
3 days
Test Coverage
/*
 * Copyright 2014 Thoughtworks Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define([
    'flight/lib/component',
    'component/mixins/with_auth_token_from_hash',
    'component/mixins/repositories_urls',
    'component/templates/popover_template'
],
  function (defineComponent, withAuthTokeFromHash, repositoriesURLs, withPopoverTemplate) {
    return defineComponent(githubIssues, withAuthTokeFromHash, repositoriesURLs, withPopoverTemplate);

    function githubIssues() {

      this.defaultAttrs({
        issues: []
      });


      this.createIssue = function (ev, data) {
        var url, repositoryURL;
        repositoryURL = this.getURLFromProject(data.projectName);
        url = this.repoIssuesURL(repositoryURL);

        $.ajax({
          type: 'POST',
          url: url,
          data: JSON.stringify({
            'title': data.issueTitle,
            'body': data.issueBody,
            'labels': ["0 - Backlog"]
          }),
          success: function (response, status, xhr) {
            response.projectName = data.projectName;
            this.trigger("ui:add:issue", {
              "issue": response
            })
          }.bind(this)
        });


      };

      this.addIssue = function (ev, data) {
        this.trigger('data:issues:refreshed', {
          issues: data
        });
      };

      this.filterProjectsByName = function (projects, projectNames) {
        if (!projectNames) {
          return projects;
        }

        var filteredRepos = [];

        _.each(projects, function (project) {
          if ($.inArray(project.projectName, projectNames) > -1) {
            filteredRepos.push(project);
          }
        });

        return filteredRepos;
      };

     this.getIssuesFromProjects = function (projects) {
      var allIssues = [];

      _.filter(projects, function(project){return project.issues}).
        forEach(function(project,index) {
          var issuesArrayJson = project.issues || [];

          _.each(issuesArrayJson, function(issue,index) {
            issue.projectName = project.projectName;
            issue.repoUrl = this.getRepoURLFromIssue(issue.url);
            allIssues.push(issue);
          }.bind(this));

        }.bind(this));
        return allIssues;

      };

      this.getRepoURLFromIssue = function (issueUrl) {
        var delimeter = '/';
        if (issueUrl) {
          var result = issueUrl.split(delimeter).reverse().slice(2).reverse().join(delimeter) + delimeter;
          return result;
        }
      };

      this.fetchIssues = function (ev, data) {
        $(document).trigger('ui:blockUI');

        data.page = ('page' in data) ? (data.page + 1) : 1;

        var issuesPromises = this.fetchAllIssues(data.page, this.attr.blockedRepos);
        var queries = _(issuesPromises).map(function (v, k) {
          return v;
        });
        var names = _(issuesPromises).map(function (v, k) {
          return k;
        });
        $.when.apply(this, queries).done(function () {
          //var issuesResults = names.length > 1 ? arguments : [arguments];
          var issuesResults = arguments;
          var projects = _(names).map(function (name, idx) {
            return {
              'projectName': name,
              'issues': issuesResults[idx]
            };
          });

          var filteredProjects = this.filterProjectsByName(projects, data.projectName),
            issuesFromProjects = this.getIssuesFromProjects(filteredProjects);

          this.trigger('data:issues:refreshed', {
            issues: issuesFromProjects
          });

          this.attr.issues = this.attr.issues.concat(issuesFromProjects);

          if (data.page == 1) {
            this.trigger('data:issues:clearExportCsvLink');
          }

          if (issuesFromProjects.length > 0) {
            this.trigger('ui:needs:issues', data);
          } else {
            var projectIdentifiers = {
              projects: this.getAllProjectsIdentifiers(_.map(filteredProjects, function (proj) {
                return proj.projectName;
              }))
            };
            this.trigger('ui:needs:priority', projectIdentifiers);
          }
        }.bind(this));

      };

      this.assignMyselfToIssue = function (ev, assignData) {

        var user, issue, url;
        if (assignData != undefined) {
          user = assignData.user;
          issue = assignData.issue;
        }

        if (!issue) {
          return;
        }

        if (!user) {
          this.trigger(document, 'ui:needs:githubUser', {
            data: assignData,
            callback: this.assignMyselfToIssue,
            context: this
          });
          return;
        }

        if (!$('#' + issue.id + ' .empty-avatar').is(':visible')) {
          var userLogin = $('#' + issue.id + ' .assignee-avatar').attr('title');
          if (userLogin == user.login) {
            this.trigger(document, 'ui:unassign:user', assignData);
          } else {
            var issue_header = $('#' + issue.id + ' .issue-header');
            if (issue_header.children('.popover').length) {
              issue_header.children('.popover').remove();
            } else {
              var that = this,

                popover_confirm = $('<div class="popover-confirm"></div>'),
                popover_confirm_yes = $('<button type="button" class="btn btn-default">Unassign</button>'),
                popover_confirm_no = $('<button type="button" class="btn btn-default">Cancel</button>'),
                pop = $(that.popover({
                  title: '',
                  body: ''
                })).appendTo(issue_header);

              popover_confirm_yes.bind('click', function () {
                that.trigger(document, 'ui:unassign:user', assignData);
                pop.remove();
              }).appendTo(popover_confirm);
              popover_confirm_no.bind('click', function () {
                pop.remove();
              }).appendTo(popover_confirm);

              pop.children('.popover-header').children('h2').html("Assignee: <strong>" + userLogin + "</strong>");
              popover_confirm.appendTo(pop.children('.popover-body'));
            }
          }
          return;
        }

        url = issue.url + "?access_token=" + this.getCurrentAuthToken();

        $('#' + issue.id + ' .empty-avatar').toggleClass('loading');
        $('#' + issue.id + ' .empty-avatar-label').hide();

        $.ajax({
          type: 'PATCH',
          url: url,
          data: JSON.stringify({
            assignee: user.login
          }),
          success: function (response, status, xhr) {
            $('#' + issue.id + ' .assigns-myself').toggleClass('assigned');
            $('#' + issue.id + ' .assignee-avatar').attr('src', user.avatar_url);
            $('#' + issue.id + ' .assignee-avatar').attr('title', user.login);
            $('#' + issue.id + ' .assignee-avatar').show();
            $('#' + issue.id + ' .empty-avatar').toggleClass('loading').hide();
            $('#' + issue.id + ' .empty-label').hide();
          },
          error: function () {
            $('#' + issue.id + ' .empty-avatar').toggleClass('loading').hide();
            $('#' + issue.id + ' .empty-avatar-label').show();
          }
        });
      };

      this.unassignMyselfToIssue = function (ev, assignData) {
        var user, issue, url, currentData;
        if (assignData != undefined) {
          user = assignData.user;
          issue = assignData.issue;
        }

        if (!issue) {
          return;
        }

        if (!user) {
          this.trigger(document, 'ui:needs:githubUser', {
            data: assignData,
            callback: this.assignMyselfToIssue,
            context: this
          });
          return;
        }

        url = issue.url + "?access_token=" + this.getCurrentAuthToken();

        currentData = {
          src: $('#' + issue.id + ' .assignee-avatar').attr('src'),
          title: $('#' + issue.id + ' .assignee-avatar').attr('title')
        };

        $('#' + issue.id + ' .assignee-avatar').attr('src', '/img/ajax-loader.gif');
        $('#' + issue.id + ' .assignee-avatar').attr('title', 'loading...');

        $.ajax({
          type: 'PATCH',
          url: url,
          data: JSON.stringify({
            assignee: ''
          }),
          success: function (response, status, xhr) {
            $('#' + issue.id + ' .assigns-myself').toggleClass('assigned');
            $('#' + issue.id + ' .assignee-avatar').attr('src', '').hide();
            $('#' + issue.id + ' .assignee-avatar').attr('title', '').hide();
            $('#' + issue.id + ' .empty-avatar').show();
            $('#' + issue.id + ' .empty-avatar-label').show();
            $('#' + issue.id + ' .empty-label').show();
          },
          error: function () {
            $('#' + issue.id + ' .assignee-avatar').attr('src', currentData.src);
            $('#' + issue.id + ' .assignee-avatar').attr('title', currentData.title);
          }
        });
      };


      this.updateDraggable = function (event, ui) {

        var label = this.parseLabel(event.target.id);

        var issueMovedParam = {
          label: label,
          element: this.DOMObjectToIssueMovedParam(ui.item[0]),
          previousElement: this.DOMObjectToIssueMovedParam(ui.item[0].previousElementSibling),
          nextElement: this.DOMObjectToIssueMovedParam(ui.item[0].nextElementSibling)
        };

        this.trigger(document, 'data:issues:issueMoved', issueMovedParam);
      };

      this.draggable = function (ev, data) {
        var customColumns = _.map(data.boardColumns, function (column) {
          return '.' + column;
        });
        var draggables = ['.backlog', '.done'].concat(customColumns);
        $(draggables.join(', ')).sortable({
          items: '.issue',
          connectWith: '.list-group',
          cancel: '.popover',
          update: this.updateDraggable.bind(this),
          receive: function (event, ui) {
            var label, url, oldLabel, state;

            if (!this.getCurrentAuthToken()) {
              this.trigger(document, 'ui:needs:githubUser');
              return;
            }

            url = this.getIssueUrlFromDraggable(ui);
            label = this.parseLabel(event.target.id);
            oldLabel = this.parseLabel(ui.sender[0].id);
            state = this.getState(event.target.className);

            $('.panel-heading.backlog-header .issues-count').text(' (' + $('.issue-track.backlog .issue').length + ')');
            $('.backlog-vertical-title .issues-count').text(' (' + $('.issue-track.backlog .issue').length + ')');

            _.each(customColumns, function (draggable) {
              $('.panel-heading' + draggable + '-header .issues-count').text(' (' + $('.issue-track' + draggable + ' .issue').length + ')');
            });

            if (label == "4 - Done") {
              this.triggerRocketAnimation();
              $.ajax({
                type: 'PATCH',
                url: url + this.getAccessTokenParam(),
                data: JSON.stringify({
                  state: "closed"
                })
              });
            } else {
              $.ajax({
                type: 'POST',
                url: url + "/labels" + this.getAccessTokenParam(),
                data: JSON.stringify([label])
              });
            }

            $.ajax({
              type: 'DELETE',
              url: url + "/labels/" + oldLabel + this.getAccessTokenParam()
            });
          }.bind(this)
        }).disableSelection();
      };

      this.triggerRocketAnimation = function () {
        $(".panel-heading.done img.plain").hide();
        $(".panel-heading.done h3").css('opacity', 0);
        $(".panel-heading.done .issues-count").css('opacity', 0);
        $(".panel-heading.done img.colored").show().animate({
          top: '-650px'
        }, 2000, 'easeInBack', function () {
          $(".panel-heading.done img.colored").hide().css('top', 0);

          $(".panel-heading.done h3").text('Liftoff! We Have a Liftoff!');
          $(".panel-heading.done h3").css('color', '#5dc66c');
          $(".panel-heading.done h3").animate({
            opacity: 1
          }, 2000);

          $(".panel-heading.done .check-done").fadeIn(2000, function () {
            $(".panel-heading.done .check-done").hide();

            $(".panel-heading.done h3").css('opacity', 0);
            $(".panel-heading.done h3").text('Drop here to launch');
            $(".panel-heading.done h3").css('color', '#aaa');

            $(".panel-heading.done img.plain").fadeIn(600);
            $(".panel-heading.done h3").animate({
              opacity: 1
            }, 600);
            $(".panel-heading.done .issues-count").animate({
              opacity: 1
            }, 600);
          });
        });
      };

      this.DOMObjectToIssueMovedParam = function (element) {
        var returnObject = {
          id: 0,
          priority: 0
        };
        if (element && element.id) {
          returnObject.id = element.id;
          returnObject.priority = element.dataset.priority;
        }
        var projectUrl = $(element).find('.issue-header a')[1];
        if (projectUrl) {
          returnObject.project = this.getProjectIdentifier(projectUrl.href) || '';
        }
        return returnObject;
      };

      this.parseLabel = function (label) {
        var fullLabel = '';
        label = label.split('-');

        for (var i = 1; i < label.length; i++) {
          var firstLetter = label[i][0];
          fullLabel = fullLabel + firstLetter.toUpperCase() + label[i].substring(1) + ' ';
        }

        fullLabel = label[0] + ' - ' + fullLabel;
        return fullLabel.trim();
      };

      this.getIssueUrlFromDraggable = function (ui) {
        return ui.item[0].childNodes[0].childNodes[1].href.replace('github.com/', 'api.github.com/repos/');
      };

      this.getAccessTokenParam = function () {
        return "?access_token=" + this.getCurrentAuthToken();
      };

      this.getState = function (className) {
        return className.search('done') != -1 ? 'closed' : 'open';
      };

      this.changeNewIssueLink = function (event, projectName) {
        $(".link").attr("href", this.newIssueURL(projectName));
      };

      this.blockUI = function () {
        $.blockUI();
      };

      this.unblockUI = function (e, timeout) {
        setTimeout($.unblockUI, (timeout || 0));
      };

      this.mountExportClick = function (ev, data) {
        this.trigger('data:issues:mountExportCsvLink', {
          issues: this.attr.issues
        });
      };

      this.clearIssues = function () {
        this.attr.issues = [];
      };


      this.after('initialize', function () {
        this.on('ui:needs:issues', this.fetchIssues);
        this.on('ui:add:issue', this.addIssue);
        this.on('ui:create:issue', this.createIssue);
        this.on('ui:assigns:user', this.assignMyselfToIssue);
        this.on('data:githubUser:here', this.assignMyselfToIssue);
        this.on('ui:draggable', this.draggable);
        this.on('ui:issue:createIssuesURL', this.changeNewIssueLink);
        this.on('#export_csv', 'click', this.mountExportClick);
        this.on('ui:unassign:user', this.unassignMyselfToIssue);
        this.on('ui:blockUI', this.blockUI);
        this.on('ui:unblockUI', this.unblockUI);
        this.on('ui:clear:issue', this.clearIssues);

      });
    }
  }
);