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 {
display: none;
margin: 75px 20px 0px 20px;
}

View File

@ -165,6 +165,7 @@ main.setMapShare = function setMapShare(id) {
$(document).ready(function() {
const { Observable } = window.Rx;
var CSRF_HEADER = 'X-CSRF-Token';
var setCSRFToken = function(securityToken) {
@ -548,51 +549,68 @@ $(document).ready(function() {
// keyboard shortcuts: open map
window.Mousetrap.bind('g m', toggleMap);
// Night Mode
function changeMode() {
var newValue = false;
try {
newValue = !JSON.parse(localStorage.getItem('nightMode'));
} catch (e) {
console.error('Error parsing value form local storage:', 'nightMode', e);
}
localStorage.setItem('nightMode', String(newValue));
toggleNightMode(newValue);
function addAlert(message = '', type = 'alert-info') {
return $('.flashMessage').append($(`
<div class='alert ${type}'>
<button class='close' type='button', data-dismiss='alert'>
<span class='ion-close-circled' />
</Button>
<div>${message}</div>
</div>
`));
}
function toggleNightMode(nightModeEnabled) {
var iframe = document.getElementById('map-aside-frame');
if (iframe) {
iframe.src = iframe.src;
function toggleNightMode() {
if (!main.userId) {
return addAlert('You must be logged in to use our themes.');
}
var body = $('body');
body.hide();
if (nightModeEnabled) {
body.addClass('night');
const iframe$ = document.getElementById('map-aside-frame');
const body$ = $('body');
if (iframe$) {
iframe$.src = iframe$.src;
}
body$.hide();
let updateThemeTo;
if (body$.hasClass('night')) {
body$.removeClass('night');
updateThemeTo = 'default';
} else {
body.removeClass('night');
body$.addClass('night');
updateThemeTo = 'night';
}
body.fadeIn('100');
}
if (typeof localStorage.getItem('nightMode') !== 'undefined') {
var oldVal = false;
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 {
oldVal = JSON.parse(localStorage.getItem('nightMode'));
} catch (e) {
console.error('Error parsing value form local storage:', 'nightMode', e);
message = JSON.parse(err.responseText).error.message;
} catch (error) {
return null;
}
toggleNightMode(oldVal);
$('.nightMode-btn').on('click', function() {
changeMode();
if (!message) {
return null;
}
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
window.Mousetrap.bind('g t n', changeMode);
window.Mousetrap.bind('g n n', () => {
// 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.prototype.update$ = function update$(updateData) {
const id = this.getId();

View File

@ -188,6 +188,10 @@
},
"timezone": {
"type": "string"
},
"theme": {
"type": "string",
"default": "default"
}
},
"validations": [],
@ -242,6 +246,13 @@
"principalId": "$owner",
"permission": "ALLOW",
"property": "updateEmail"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "updateTheme"
}
],
"methods": []

View File

@ -6,6 +6,10 @@ export default function globalLocals() {
if (req.csrfToken) {
res.expose({ token: res.locals._csrf }, 'csrf');
}
res.locals.theme = req.user && req.user.theme ||
req.cookies.theme ||
'default';
next();
};
}

View File

@ -6,7 +6,7 @@ export function ifNoUserRedirectTo(url, message, type = 'errors') {
}
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);

View File

@ -5,7 +5,7 @@ block content
h2.text-center Actions
.row
.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
.col-xs-12
if (!user.isGithubCool)

View File

@ -8,7 +8,7 @@ html(lang='en')
include partials/scripts
block content
else
body.no-top-and-bottom-margins
body.no-top-and-bottom-margins(class=theme !== 'default' ? theme : '')
include partials/scripts
include partials/navbar
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 #3") Mock Interview #3
.spacer
include ../partials/flash

View File

@ -1,6 +1,6 @@
.container
.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)
.alert.alert-danger.fade.in
button.close(type='button', data-dismiss='alert')

View File

@ -7,5 +7,11 @@ script.
ga('require', 'displayfeatures');
ga('send', 'pageview');
// 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', 'main.js'))