Merge branch 'BerkeleyTrue-fix/theme-flash' into staging
This commit is contained in:
		@@ -64,7 +64,6 @@ body.top-and-bottom-margins {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body.no-top-and-bottom-margins {
 | 
					body.no-top-and-bottom-margins {
 | 
				
			||||||
  display: none;
 | 
					 | 
				
			||||||
  margin: 75px 20px 0px 20px;
 | 
					  margin: 75px 20px 0px 20px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -165,6 +165,7 @@ main.setMapShare = function setMapShare(id) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
$(document).ready(function() {
 | 
					$(document).ready(function() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { Observable } = window.Rx;
 | 
				
			||||||
  var CSRF_HEADER = 'X-CSRF-Token';
 | 
					  var CSRF_HEADER = 'X-CSRF-Token';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var setCSRFToken = function(securityToken) {
 | 
					  var setCSRFToken = function(securityToken) {
 | 
				
			||||||
@@ -548,51 +549,68 @@ $(document).ready(function() {
 | 
				
			|||||||
  // keyboard shortcuts: open map
 | 
					  // keyboard shortcuts: open map
 | 
				
			||||||
  window.Mousetrap.bind('g m', toggleMap);
 | 
					  window.Mousetrap.bind('g m', toggleMap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Night Mode
 | 
					  function addAlert(message = '', type = 'alert-info') {
 | 
				
			||||||
  function changeMode() {
 | 
					    return $('.flashMessage').append($(`
 | 
				
			||||||
    var newValue = false;
 | 
					      <div class='alert ${type}'>
 | 
				
			||||||
    try {
 | 
					        <button class='close' type='button', data-dismiss='alert'>
 | 
				
			||||||
      newValue = !JSON.parse(localStorage.getItem('nightMode'));
 | 
					          <span class='ion-close-circled' />
 | 
				
			||||||
    } catch (e) {
 | 
					        </Button>
 | 
				
			||||||
      console.error('Error parsing value form local storage:', 'nightMode', e);
 | 
					        <div>${message}</div>
 | 
				
			||||||
    }
 | 
					      </div>
 | 
				
			||||||
    localStorage.setItem('nightMode', String(newValue));
 | 
					    `));
 | 
				
			||||||
    toggleNightMode(newValue);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function toggleNightMode(nightModeEnabled) {
 | 
					  function toggleNightMode() {
 | 
				
			||||||
    var iframe = document.getElementById('map-aside-frame');
 | 
					    if (!main.userId) {
 | 
				
			||||||
    if (iframe) {
 | 
					      return addAlert('You must be logged in to use our themes.');
 | 
				
			||||||
      iframe.src = iframe.src;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var body = $('body');
 | 
					    const iframe$ = document.getElementById('map-aside-frame');
 | 
				
			||||||
    body.hide();
 | 
					    const body$ = $('body');
 | 
				
			||||||
    if (nightModeEnabled) {
 | 
					    if (iframe$) {
 | 
				
			||||||
      body.addClass('night');
 | 
					      iframe$.src = iframe$.src;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    body$.hide();
 | 
				
			||||||
 | 
					    let updateThemeTo;
 | 
				
			||||||
 | 
					    if (body$.hasClass('night')) {
 | 
				
			||||||
 | 
					      body$.removeClass('night');
 | 
				
			||||||
 | 
					      updateThemeTo = 'default';
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      body.removeClass('night');
 | 
					      body$.addClass('night');
 | 
				
			||||||
 | 
					      updateThemeTo = 'night';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    body.fadeIn('100');
 | 
					    body$.fadeIn('100');
 | 
				
			||||||
 | 
					    const options = {
 | 
				
			||||||
 | 
					      url: `/api/users/${main.userId}/update-theme`,
 | 
				
			||||||
 | 
					      type: 'POST',
 | 
				
			||||||
 | 
					      data: { theme: updateThemeTo },
 | 
				
			||||||
 | 
					      dataType: 'json'
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    return $.ajax(options)
 | 
				
			||||||
 | 
					      .success(() => console.log('theme updated successfully'))
 | 
				
			||||||
 | 
					      .fail(err => {
 | 
				
			||||||
 | 
					        let message;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          message = JSON.parse(err.responseText).error.message;
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					          return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!message) {
 | 
				
			||||||
 | 
					          return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return addAlert(message);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (typeof localStorage.getItem('nightMode') !== 'undefined') {
 | 
					  Observable.merge(
 | 
				
			||||||
    var oldVal = false;
 | 
					    Observable.fromEvent($('#night-mode'), 'click'),
 | 
				
			||||||
    try {
 | 
					    Observable.create(observer => {
 | 
				
			||||||
      oldVal = JSON.parse(localStorage.getItem('nightMode'));
 | 
					      window.Mousetrap.bind('g t n', () => observer.onNext());
 | 
				
			||||||
    } catch (e) {
 | 
					    })
 | 
				
			||||||
      console.error('Error parsing value form local storage:', 'nightMode', e);
 | 
					  )
 | 
				
			||||||
    }
 | 
					    .debounce(500)
 | 
				
			||||||
    toggleNightMode(oldVal);
 | 
					    .subscribe(toggleNightMode, err => console.error(err));
 | 
				
			||||||
    $('.nightMode-btn').on('click', function() {
 | 
					 | 
				
			||||||
      changeMode();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    localStorage.setItem('nightMode', 'false');
 | 
					 | 
				
			||||||
    toggleNightMode('false');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Hot Keys
 | 
					  // Hot Keys
 | 
				
			||||||
  window.Mousetrap.bind('g t n', changeMode);
 | 
					 | 
				
			||||||
  window.Mousetrap.bind('g n n', () => {
 | 
					  window.Mousetrap.bind('g n n', () => {
 | 
				
			||||||
    // Next Challenge
 | 
					    // Next Challenge
 | 
				
			||||||
    window.location = '/challenges/next-challenge';
 | 
					    window.location = '/challenges/next-challenge';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -486,6 +486,91 @@ module.exports = function(User) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  User.prototype.updateEmail = function updateEmail(email) {
 | 
				
			||||||
 | 
					    if (this.email && this.email === email) {
 | 
				
			||||||
 | 
					      return Promise.reject(new Error(
 | 
				
			||||||
 | 
					        `${email} is already associated with this account.`
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return User.doesExist(null, email)
 | 
				
			||||||
 | 
					      .then(exists => {
 | 
				
			||||||
 | 
					        if (exists) {
 | 
				
			||||||
 | 
					          return Promise.reject(
 | 
				
			||||||
 | 
					            new Error(`${email} is already associated with another account.`)
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return this.update$({ email }).toPromise();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  User.remoteMethod(
 | 
				
			||||||
 | 
					    'updateEmail',
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      isStatic: false,
 | 
				
			||||||
 | 
					      description: 'updates the email of the user object',
 | 
				
			||||||
 | 
					      accepts: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          arg: 'email',
 | 
				
			||||||
 | 
					          type: 'string',
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      returns: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          arg: 'status',
 | 
				
			||||||
 | 
					          type: 'object'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      http: {
 | 
				
			||||||
 | 
					        path: '/update-email',
 | 
				
			||||||
 | 
					        verb: 'POST'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  User.themes = {
 | 
				
			||||||
 | 
					    night: true,
 | 
				
			||||||
 | 
					    default: true
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  User.prototype.updateTheme = function updateTheme(theme) {
 | 
				
			||||||
 | 
					    if (!this.constructor.themes[theme]) {
 | 
				
			||||||
 | 
					      const err = new Error(
 | 
				
			||||||
 | 
					        'Theme is not valid.'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      err.messageType = 'info';
 | 
				
			||||||
 | 
					      err.userMessage = err.message;
 | 
				
			||||||
 | 
					      return Promise.reject(err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return this.update$({ theme })
 | 
				
			||||||
 | 
					      .map({ updatedTo: theme })
 | 
				
			||||||
 | 
					      .toPromise();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  User.remoteMethod(
 | 
				
			||||||
 | 
					    'updateTheme',
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      isStatic: false,
 | 
				
			||||||
 | 
					      description: 'updates the users chosen theme',
 | 
				
			||||||
 | 
					      accepts: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          arg: 'theme',
 | 
				
			||||||
 | 
					          type: 'string',
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      returns: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          arg: 'status',
 | 
				
			||||||
 | 
					          type: 'object'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      http: {
 | 
				
			||||||
 | 
					        path: '/update-theme',
 | 
				
			||||||
 | 
					        verb: 'POST'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // user.updateTo$(updateData: Object) => Observable[Number]
 | 
					  // user.updateTo$(updateData: Object) => Observable[Number]
 | 
				
			||||||
  User.prototype.update$ = function update$(updateData) {
 | 
					  User.prototype.update$ = function update$(updateData) {
 | 
				
			||||||
    const id = this.getId();
 | 
					    const id = this.getId();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -188,6 +188,10 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "timezone": {
 | 
					    "timezone": {
 | 
				
			||||||
      "type": "string"
 | 
					      "type": "string"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "theme": {
 | 
				
			||||||
 | 
					      "type": "string",
 | 
				
			||||||
 | 
					      "default": "default"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "validations": [],
 | 
					  "validations": [],
 | 
				
			||||||
@@ -242,6 +246,13 @@
 | 
				
			|||||||
      "principalId": "$owner",
 | 
					      "principalId": "$owner",
 | 
				
			||||||
      "permission": "ALLOW",
 | 
					      "permission": "ALLOW",
 | 
				
			||||||
      "property": "updateEmail"
 | 
					      "property": "updateEmail"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "accessType": "EXECUTE",
 | 
				
			||||||
 | 
					      "principalType": "ROLE",
 | 
				
			||||||
 | 
					      "principalId": "$owner",
 | 
				
			||||||
 | 
					      "permission": "ALLOW",
 | 
				
			||||||
 | 
					      "property": "updateTheme"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "methods": []
 | 
					  "methods": []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,10 @@ export default function globalLocals() {
 | 
				
			|||||||
    if (req.csrfToken) {
 | 
					    if (req.csrfToken) {
 | 
				
			||||||
      res.expose({ token: res.locals._csrf }, 'csrf');
 | 
					      res.expose({ token: res.locals._csrf }, 'csrf');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    res.locals.theme = req.user && req.user.theme ||
 | 
				
			||||||
 | 
					      req.cookies.theme ||
 | 
				
			||||||
 | 
					      'default';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    next();
 | 
					    next();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ export function ifNoUserRedirectTo(url, message, type = 'errors') {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    req.flash(type, {
 | 
					    req.flash(type, {
 | 
				
			||||||
      msg: message || `You must be signed to go to ${path}`
 | 
					      msg: message || `You must be signed in to access ${path}`
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return res.redirect(url);
 | 
					    return res.redirect(url);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ block content
 | 
				
			|||||||
        h2.text-center Actions
 | 
					        h2.text-center Actions
 | 
				
			||||||
        .row
 | 
					        .row
 | 
				
			||||||
          .col-xs-12
 | 
					          .col-xs-12
 | 
				
			||||||
            a.btn.btn-lg.btn-block.btn-primary.btn-link-social(class = "nightMode-btn") Night Mode
 | 
					            a#night-mode.btn.btn-lg.btn-block.btn-primary.btn-link-social Night Mode
 | 
				
			||||||
        .row
 | 
					        .row
 | 
				
			||||||
          .col-xs-12
 | 
					          .col-xs-12
 | 
				
			||||||
              if (!user.isGithubCool)
 | 
					              if (!user.isGithubCool)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,9 +8,9 @@ html(lang='en')
 | 
				
			|||||||
          include partials/scripts
 | 
					          include partials/scripts
 | 
				
			||||||
          block content
 | 
					          block content
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
      body.no-top-and-bottom-margins
 | 
					      body.no-top-and-bottom-margins(class=theme !== 'default' ? theme : '')
 | 
				
			||||||
        include partials/scripts
 | 
					          include partials/scripts
 | 
				
			||||||
        include partials/navbar
 | 
					          include partials/navbar
 | 
				
			||||||
        include partials/flash
 | 
					          include partials/flash
 | 
				
			||||||
        block content
 | 
					          block content
 | 
				
			||||||
        include partials/footer
 | 
					          include partials/footer
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
extends ../layout-wide
 | 
					extends ../layout-wide
 | 
				
			||||||
block content
 | 
					block content
 | 
				
			||||||
      .mapWrapper
 | 
					    .mapWrapper
 | 
				
			||||||
        .text-center.map-fixed-header
 | 
					        .text-center.map-fixed-header
 | 
				
			||||||
            p Challenges required for certifications are marked with a *
 | 
					            p Challenges required for certifications are marked with a *
 | 
				
			||||||
            .row.map-buttons
 | 
					            .row.map-buttons
 | 
				
			||||||
@@ -9,7 +9,7 @@ block content
 | 
				
			|||||||
                .input-group
 | 
					                .input-group
 | 
				
			||||||
                    input#map-filter.form-control(type="text" placeholder="Type a challenge name" autocomplete="off" value="")
 | 
					                    input#map-filter.form-control(type="text" placeholder="Type a challenge name" autocomplete="off" value="")
 | 
				
			||||||
                    span.input-group-addon
 | 
					                    span.input-group-addon
 | 
				
			||||||
                       i.fa.fa-search
 | 
					                        i.fa.fa-search
 | 
				
			||||||
            hr
 | 
					            hr
 | 
				
			||||||
            include ../partials/flash
 | 
					            include ../partials/flash
 | 
				
			||||||
        #accordion.map-accordion
 | 
					        #accordion.map-accordion
 | 
				
			||||||
@@ -122,3 +122,4 @@ block content
 | 
				
			|||||||
                        p.challenge-title.disabled.text-primary.ion-locked.padded-ionic-icon.negative-15(name="Mock Interview #2") Mock Interview #2
 | 
					                        p.challenge-title.disabled.text-primary.ion-locked.padded-ionic-icon.negative-15(name="Mock Interview #2") Mock Interview #2
 | 
				
			||||||
                        p.challenge-title.disabled.text-primary.ion-locked.padded-ionic-icon.negative-15(name="Mock Interview #3") Mock Interview #3
 | 
					                        p.challenge-title.disabled.text-primary.ion-locked.padded-ionic-icon.negative-15(name="Mock Interview #3") Mock Interview #3
 | 
				
			||||||
          .spacer
 | 
					          .spacer
 | 
				
			||||||
 | 
					        include ../partials/flash
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
.container
 | 
					.container
 | 
				
			||||||
    .row.flashMessage.negative-30
 | 
					    .row.flashMessage.negative-30
 | 
				
			||||||
        .col-xs-12
 | 
					        .col-xs-12.col-sm-8.col-sm-offset-2.col-md-6.col-md-offset-3
 | 
				
			||||||
            if (messages.errors || messages.error)
 | 
					            if (messages.errors || messages.error)
 | 
				
			||||||
              .alert.alert-danger.fade.in
 | 
					              .alert.alert-danger.fade.in
 | 
				
			||||||
                button.close(type='button', data-dismiss='alert')
 | 
					                button.close(type='button', data-dismiss='alert')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,5 +7,11 @@ script.
 | 
				
			|||||||
    ga('require', 'displayfeatures');
 | 
					    ga('require', 'displayfeatures');
 | 
				
			||||||
    ga('send', 'pageview');
 | 
					    ga('send', 'pageview');
 | 
				
			||||||
// Leave the below lines alone!
 | 
					// Leave the below lines alone!
 | 
				
			||||||
 | 
					script.
 | 
				
			||||||
 | 
					  (function(global) {
 | 
				
			||||||
 | 
					    global.main = global.main || {};
 | 
				
			||||||
 | 
					    global.main.isLoggedIn = !{JSON.stringify(!!user)};
 | 
				
			||||||
 | 
					    global.main.userId = !{JSON.stringify(user && user.id || false)};
 | 
				
			||||||
 | 
					  }(window))
 | 
				
			||||||
script(src=rev('/js', 'vendor-main.js'))
 | 
					script(src=rev('/js', 'vendor-main.js'))
 | 
				
			||||||
script(src=rev('/js', 'main.js'))
 | 
					script(src=rev('/js', 'main.js'))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user