Merge branch 'BerkeleyTrue-fix/theme-flash' into staging

This commit is contained in:
Quincy Larson
2016-05-12 21:28:55 -07:00
11 changed files with 173 additions and 49 deletions

View File

@ -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;
} }

View File

@ -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`,
if (typeof localStorage.getItem('nightMode') !== 'undefined') { type: 'POST',
var oldVal = false; data: { theme: updateThemeTo },
dataType: 'json'
};
return $.ajax(options)
.success(() => console.log('theme updated successfully'))
.fail(err => {
let message;
try { try {
oldVal = JSON.parse(localStorage.getItem('nightMode')); message = JSON.parse(err.responseText).error.message;
} catch (e) { } catch (error) {
console.error('Error parsing value form local storage:', 'nightMode', e); return null;
} }
toggleNightMode(oldVal); if (!message) {
$('.nightMode-btn').on('click', function() { return null;
changeMode(); }
return addAlert(message);
}); });
} else {
localStorage.setItem('nightMode', 'false');
toggleNightMode('false');
} }
Observable.merge(
Observable.fromEvent($('#night-mode'), 'click'),
Observable.create(observer => {
window.Mousetrap.bind('g t n', () => observer.onNext());
})
)
.debounce(500)
.subscribe(toggleNightMode, err => console.error(err));
// 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';

View File

@ -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();

View File

@ -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": []

View File

@ -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();
}; };
} }

View File

@ -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);

View File

@ -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)

View File

@ -8,7 +8,7 @@ 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

View File

@ -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

View File

@ -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')

View File

@ -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'))