CartoDB/cartodb20

View on GitHub
lib/assets/javascripts/dashboard/components/dashboard-header-view.js

Summary

Maintainability
A
55 mins
Test Coverage
const $ = require('jquery');
const CoreView = require('backbone/core-view');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const logoTemplate = require('./dashboard-header/logo.tpl');
const dropdownLinkTemplate = require('./dashboard-header/breadcrumbs/dropdown-link.tpl');
const SettingsDropdownView = require('./dashboard-header/settings-dropdown-view');
const BreadcrumbsDropdown = require('./dashboard-header/breadcrumbs/dropdown-view');
const UserNotificationsView = require('./dashboard-header/notifications/user-notifications-view');

const REQUIRED_OPTS = [
  'viewModel',
  'configModel'
];

/**
 * Responsible for the header part of the layout.
 * It's currently pre-rendered server-side, why the header element is required to be given when instantiating the view.
 */
module.exports = CoreView.extend({
  events: {
    'click .js-breadcrumb-dropdown': '_createBreadcrumbsDropdown',
    'click .js-settings-dropdown': '_createSettingsDropdown'
  },

  initialize: function (options) {
    checkAndBuildOpts(options, REQUIRED_OPTS, this);

    if (!this.options.el) {
      throw new Error('el element is required');
    }

    this.router = this.options.router;
    this._initBinds();
  },

  render: function () {
    this.clearSubViews();

    this._renderBreadcrumbsDropdownLink();
    this._renderNotifications();
    this._renderLogoLink();

    return this;
  },

  _initBinds: function () {
    this._viewModel.bind('change', this._renderBreadcrumbsDropdownLink, this);
    this.listenTo(this._viewModel, 'change', this._renderBreadcrumbsDropdownLink);

    if (this.router) {
      this.listenTo(this.router.model, 'change', this._onRouterChange);
    }
    if (this.collection) {
      this.listenTo(this.collection, 'reset', this._stopLogoAnimation);
      this.listenTo(this.collection, 'error', this._onCollectionError);
    }
  },

  _onCollectionError: function (col, e, opts) {
    // Old requests can be stopped, so aborted requests are not
    // considered as an error
    if (!e || (e && e.statusText !== 'abort')) {
      this._stopLogoAnimation();
    }
  },

  // TODO: Not sure if the changes made here are correct, because of backbone version changes
  _onRouterChange: function (m, c) {
    if (m.changed && !m.changed.content_type && this.collection.total_user_entries > 0) {
      this._startLogoAnimation();
    }
  },

  _startLogoAnimation: function () {
    this.$('.Logo').addClass('is-loading');
  },

  _stopLogoAnimation: function () {
    this.$('.Logo').removeClass('is-loading');
  },

  _renderBreadcrumbsDropdownLink: function () {
    this.$('.js-breadcrumb-dropdown').html(
      dropdownLinkTemplate({
        title: this._viewModel.breadcrumbTitle(),
        dropdownEnabled: this._viewModel.isBreadcrumbDropdownEnabled()
      })
    );
  },

  _renderNotifications: function () {
    var userNotifications = new UserNotificationsView({
      user: this.model,
      configModel: this._configModel,
      organizationNotifications: this.options.organizationNotifications
    });

    this.$('.js-user-notifications').html(userNotifications.render().el);
    this.addView(userNotifications);
  },

  _renderLogoLink: function () {
    this.$('.js-logo').html(
      logoTemplate({
        homeUrl: this.model.viewUrl().dashboard(),
        googleEnabled: this.model.featureEnabled('google_maps')
      })
    );
  },

  _createSettingsDropdown: function (event) {
    this.killEvent(event);

    this._setupDropdown(new SettingsDropdownView({
      target: $(event.target),
      model: this.model, // a user model
      configModel: this._configModel,
      horizontalOffset: 15
    }));
  },

  _createBreadcrumbsDropdown: function (ev) {
    if (this._viewModel.isBreadcrumbDropdownEnabled()) {
      this.killEvent(ev);
      this._setupDropdown(new BreadcrumbsDropdown({
        target: $(ev.target),
        model: this.model,
        viewModel: this._viewModel,
        router: this.router, // optional
        tick: 'center',
        template: require('dashboard/components/dashboard-header/breadcrumbs/dropdown.tpl'),
        horizontalOffset: this.options.breadcrumbsDropdownOffset
      }));
    }
  },

  _setupDropdown: function (dropdownView) {
    this._closeAnyOtherOpenDialogs();
    this._previousDropDown = dropdownView;
    this.addView(dropdownView);

    dropdownView.on('onDropdownHidden', function () {
      dropdownView.clean();
    }, this);

    dropdownView.render();
    dropdownView.open();
  },

  _closeAnyOtherOpenDialogs: function () {
    // TODO: This is not how it used to work, it used to listen to a global event
    if (this._previousDropDown) {
      this._previousDropDown.hide();
    }
  }
});