From 9cd5c98004cf305d3a79950dce36094ce75e1292 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 7 Dec 2015 17:43:04 -0500 Subject: [PATCH 01/10] Make Jar formatting consistent with everything else --- src/lib/jar.js | 56 ++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/lib/jar.js b/src/lib/jar.js index 951c2f2ab..78f6b8ea1 100644 --- a/src/lib/jar.js +++ b/src/lib/jar.js @@ -9,41 +9,39 @@ var xhr = require('xhr'); * set(name, value) – synchronously sets the cookie * use(name, uri, callback) – can by sync or async, gets cookie from the uri if not there. */ -var Jar = {}; - -Jar.get = function (name, callback) { +var Jar = { + get: function (name, callback) { // Get cookie by name var obj = cookie.parse(document.cookie) || {}; - // Handle optional callback - if (typeof callback === 'function') { - if (typeof obj === 'undefined') return callback('Cookie not found.'); - return callback(null, obj[name]); - } + // Handle optional callback + if (typeof callback === 'function') { + if (typeof obj === 'undefined') return callback('Cookie not found.'); + return callback(null, obj[name]); + } - return obj[name]; -}; + return obj[name]; + }, + use: function (name, uri, callback) { + // Attempt to get cookie + Jar.get(name, function (err, obj) { + if (typeof obj !== 'undefined') return callback(null, obj); -Jar.use = function (name, uri, callback) { - // Attempt to get cookie - Jar.get(name, function (err, obj) { - if (typeof obj !== 'undefined') return callback(null, obj); - - // Make XHR request to cookie setter uri - xhr({ - uri: uri - }, function (err) { - if (err) return callback(err); - Jar.get(name, callback); + // Make XHR request to cookie setter uri + xhr({ + uri: uri + }, function (err) { + if (err) return callback(err); + Jar.get(name, callback); + }); }); - }); -}; - -Jar.set = function (name, value) { - var obj = cookie.serialize(name, value); - var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString(); - var path = '; path=/'; - document.cookie = obj + expires + path; + }, + set: function (name, value) { + var obj = cookie.serialize(name, value); + var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString(); + var path = '; path=/'; + document.cookie = obj + expires + path; + } }; module.exports = Jar; From 96bc1b1ab1c414c547483a03540251d7a4f63c42 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 8 Dec 2015 15:14:16 -0500 Subject: [PATCH 02/10] Add method for reading session cookie Assumes the session cookie is stored as JSON which may or may not have been compressed via zlib (indicated by a leading `.`), which is then base64-encoded, and made URL-safe by replacing all `+` and `/` characters with `-` and `_` respectively. --- package.json | 1 + src/lib/jar.js | 28 ++++++++++++++++++++++++++++ static/js/lib/polyfill.min.js | 6 ++++++ 3 files changed, 35 insertions(+) diff --git a/package.json b/package.json index 098037279..2f838c19f 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "lodash.range": "3.0.1", "minilog": "2.0.8", "node-sass": "3.3.3", + "pako": "0.2.8", "po2icu": "git://github.com/LLK/po2icu.git#develop", "react-addons-test-utils": "0.14.7", "react-modal": "0.6.1", diff --git a/src/lib/jar.js b/src/lib/jar.js index 78f6b8ea1..266f56ce6 100644 --- a/src/lib/jar.js +++ b/src/lib/jar.js @@ -1,5 +1,6 @@ var cookie = require('cookie'); var xhr = require('xhr'); +var pako = require('pako'); /** * Module that handles coookie interactions. @@ -10,6 +11,33 @@ var xhr = require('xhr'); * use(name, uri, callback) – can by sync or async, gets cookie from the uri if not there. */ var Jar = { + unsign: function (value, callback) { + // Return the usable content portion of a signed, compressed cookie + if (!value) return callback('No value to unsign'); + try { + var b64Data = value.split(':')[0]; + var decompress = false; + if (b64Data[0] === '.') { + decompress = true; + b64Data = b64Data.substring(1); + } + + // Django makes its base64 strings url safe by replacing + and / with - and _ respectively + b64Data = b64Data.replace(/[-_]/g, function (c) {return {'-':'+', '_':'/'}[c]; }); + var strData = atob(b64Data); + + if (decompress) { + var charData = strData.split('').map(function (c) { return c.charCodeAt(0); }); + var binData = new Uint8Array(charData); + var data = pako.inflate(binData); + strData = String.fromCharCode.apply(null, new Uint16Array(data)); + } + + return callback(null, strData); + } catch (e) { + return callback(e); + } + }, get: function (name, callback) { // Get cookie by name var obj = cookie.parse(document.cookie) || {}; diff --git a/static/js/lib/polyfill.min.js b/static/js/lib/polyfill.min.js index e152e90e0..f8863dd3a 100644 --- a/static/js/lib/polyfill.min.js +++ b/static/js/lib/polyfill.min.js @@ -26,6 +26,12 @@ */ (function(){try{new e("test")}catch(t){var e=function(t,e){var n;return e=e||{bubbles:!1,cancelable:!1,detail:void 0},n=document.createEvent("CustomEvent"),n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n};e.prototype=window.Event.prototype,window.CustomEvent=e}})(); +/*! + * https://github.com/davidchambers/Base64.js + * see https://github.com/davidchambers/Base64.js/blob/master/LICENSE + */ +!function(){function t(t){this.message=t}var r="undefined"!=typeof exports?exports:this,e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t.prototype=new Error,t.prototype.name="InvalidCharacterError",r.btoa||(r.btoa=function(r){for(var o,n,a=String(r),i=0,c=e,d="";a.charAt(0|i)||(c="=",i%1);d+=c.charAt(63&o>>8-i%1*8)){if(n=a.charCodeAt(i+=.75),n>255)throw new t("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|n}return d}),r.atob||(r.atob=function(r){var o=String(r).replace(/=+$/,"");if(o.length%4==1)throw new t("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,a,i=0,c=0,d="";a=o.charAt(c++);~a&&(n=i%4?64*n+a:a,i++%4)?d+=String.fromCharCode(255&n>>(-2*i&6)):0)a=e.indexOf(a);return d})}(); + /*! * https://github.com/andyearnshaw/Intl.js * @license The MIT License (MIT) Copyright (c) 2013 Andy Earnshaw From 32b7d5a41b408ba1791f73c1c06b9480f342505a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 22 Mar 2016 14:43:07 -0400 Subject: [PATCH 03/10] Retrieve token from session cookie --- src/redux/actions.js | 42 +++++++++++++++++++++++++++++++++++++++--- src/redux/reducer.js | 20 ++++++++++++++++++-- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/redux/actions.js b/src/redux/actions.js index 740a951ee..c82973ef0 100644 --- a/src/redux/actions.js +++ b/src/redux/actions.js @@ -1,10 +1,13 @@ var keyMirror = require('keymirror'); var api = require('../mixins/api.jsx').api; +var jar = require('../lib/jar.js'); var Types = keyMirror({ - REFRESH_SESSION: null, SET_SESSION: null, - SET_SESSION_ERROR: null + SET_SESSION_ERROR: null, + SET_TOKEN: null, + SET_TOKEN_ERROR: null, + USE_TOKEN: null }); var Actions = { @@ -36,12 +39,45 @@ var Actions = { if (body.banned) { return window.location = url; } else { - return dispatch(Actions.setSession(body)); + dispatch(Actions.getToken()); + dispatch(Actions.setSession(body)); + return; } } }); }; }, + + getToken: function () { + return function (dispatch) { + jar.get('scratchsessionsid', function (err, value) { + if (err) return dispatch(Actions.setTokenError(err)); + jar.unsign(value, function (err, contents) { + if (err) return dispatch(Actions.setTokenError(err)); + try { + var sessionData = JSON.parse(contents); + } catch (err) { + return dispatch(Actions.setTokenError(err)); + } + return dispatch(Actions.setToken(sessionData.token)); + }); + }); + } + }, + + setToken: function (token) { + return { + type: Types.SET_TOKEN, + token: token + }; + }, + + setTokenError: function (error) { + return { + type: Types.SET_SESSION_ERROR, + error: error + }; + } }; module.exports = Actions; diff --git a/src/redux/reducer.js b/src/redux/reducer.js index 2ae881afe..d9b522677 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -5,7 +5,6 @@ var actionTypes = require('./actions.js').types; var sessionReducer = function (state, action) { // Reducer for handling changes to session state - if (typeof state === 'undefined') { state = {}; } @@ -20,8 +19,25 @@ var sessionReducer = function (state, action) { } }; +var tokenReducer = function (state, action) { + // Reducer for updating the api token + if (typeof state === 'undefined') { + state = ''; + } + switch (action.type) { + case actionTypes.SET_TOKEN: + return action.token; + case actionTypes.SET_TOKEN_ERROR: + // TODO: do something with the error + return state; + default: + return state; + } +}; + var appReducer = combineReducers({ - session: sessionReducer + session: sessionReducer, + token: tokenReducer }); module.exports = appReducer; From 1eb8c537c18acfef6ac8e11a7afd0c6647be6bc6 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 22 Mar 2016 14:43:44 -0400 Subject: [PATCH 04/10] Add method for using token in api requests Requires a change on the api to read the token from the querystring. --- src/mixins/api.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mixins/api.jsx b/src/mixins/api.jsx index 2c82b9e80..47ee8faff 100644 --- a/src/mixins/api.jsx +++ b/src/mixins/api.jsx @@ -33,6 +33,13 @@ var Api = { // custom headers. defaults(opts, {useXDR: true}); delete opts.headers; + if (opts.authentication) { + var authenticationParams = ['x-token=' + opts.authentication]; + var parts = opts.uri.split('?'); + var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&'); + opts.uri = parts[0] + '?' + qs; + + } } xhr(opts, function (err, res, body) { if (err) log.error(err); @@ -50,6 +57,9 @@ var Api = { if (typeof jar.get('scratchlanguage') !== 'undefined') { opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8'; } + if (opts.authentication) { + opts.headers['X-Token'] = opts.authentication; + } if (opts.useCsrf) { jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) { if (err) return log.error('Error while retrieving CSRF token', err); From 21dffa73f0529ee8ecc365c907e98a4651568fd6 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 23 Mar 2016 09:50:41 -0400 Subject: [PATCH 05/10] Fix indentation --- src/lib/jar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/jar.js b/src/lib/jar.js index 266f56ce6..b10690105 100644 --- a/src/lib/jar.js +++ b/src/lib/jar.js @@ -39,8 +39,8 @@ var Jar = { } }, get: function (name, callback) { - // Get cookie by name - var obj = cookie.parse(document.cookie) || {}; + // Get cookie by name + var obj = cookie.parse(document.cookie) || {}; // Handle optional callback if (typeof callback === 'function') { From 339c23a64e808a2c43d29b60d1b95e965db212c8 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 23 Mar 2016 10:02:20 -0400 Subject: [PATCH 06/10] Add some sources to the unsign method --- src/lib/jar.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/jar.js b/src/lib/jar.js index b10690105..89accd5a5 100644 --- a/src/lib/jar.js +++ b/src/lib/jar.js @@ -12,7 +12,9 @@ var pako = require('pako'); */ var Jar = { unsign: function (value, callback) { - // Return the usable content portion of a signed, compressed cookie + // Return the usable content portion of a signed, compressed cookie generated by + // Django's signing module + // https://github.com/django/django/blob/stable/1.8.x/django/core/signing.py if (!value) return callback('No value to unsign'); try { var b64Data = value.split(':')[0]; @@ -23,6 +25,8 @@ var Jar = { } // Django makes its base64 strings url safe by replacing + and / with - and _ respectively + // using base64.urlsafe_b64encode + // https://docs.python.org/2/library/base64.html#base64.b64encode b64Data = b64Data.replace(/[-_]/g, function (c) {return {'-':'+', '_':'/'}[c]; }); var strData = atob(b64Data); From 19f469db48d083eb1dabdac5c2850c6a0e18143d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 23 Mar 2016 18:34:54 -0400 Subject: [PATCH 07/10] Upgrade and quiet sass-linter These warnings were annoying me because the noise makes it easy to miss real issues. The `no-mergeable-selectors` rule is one that we do want to have, but right now it asks that you merge selectors in different `@media` blocks. When the next release happens we should put that back. https://github.com/sasstools/sass-lint/issues/307 Similarly, we want `force-element-nesting` but there is a problem with that because there's no easy way to have a nested selector in a list of selectors. https://github.com/sasstools/sass-lint/issues/575 Finally, until they implement per-line overrides, we have to silence `class-name-format` because we don't have control over the ReactModal class names. It's a useful rule to keep class names consistent though. Per-line ignores should be coming soon: https://github.com/sasstools/sass-lint/issues/70 --- .sass-lint.yml | 9 + package.json | 2 +- src/components/adminpanel/adminpanel.scss | 29 +- src/components/banner/banner.scss | 13 +- src/components/box/box.scss | 6 +- src/components/carousel/carousel.scss | 2 +- src/components/intro/intro.scss | 102 +++--- .../languagechooser/languagechooser.jsx | 2 - .../languagechooser/languagechooser.scss | 3 - src/components/login/login.scss | 7 +- src/components/modal/modal.scss | 8 +- src/components/navigation/dropdown.scss | 2 +- src/components/navigation/navigation.jsx | 4 +- src/components/navigation/navigation.scss | 306 +++++++++--------- src/components/news/news.scss | 8 +- src/components/spinner/spinner.scss | 24 +- .../subnavigation/subnavigation.scss | 4 +- src/components/thumbnail/thumbnail.scss | 22 +- src/components/welcome/welcome.scss | 7 + src/main.scss | 30 +- src/views/about/about.scss | 3 +- src/views/credits/credits.scss | 3 +- src/views/hoc/hoc.scss | 2 +- 23 files changed, 324 insertions(+), 274 deletions(-) delete mode 100644 src/components/languagechooser/languagechooser.scss diff --git a/.sass-lint.yml b/.sass-lint.yml index 79136f013..e60e787eb 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -1,6 +1,8 @@ rules: + class-name-format: 0 color-literals: 2 final-newline: 2 + force-element-nesting: 0 hex-notation: 2 indentation: - 2 @@ -13,6 +15,12 @@ rules: max-depth: 4 no-css-comments: 0 no-ids: 0 + no-mergeable-selectors: 0 + no-qualifying-elements: + - 1 + - + - allow-element-with-attribute + no-transition-all: 0 property-sort-order: - 2 - @@ -21,4 +29,5 @@ rules: - 2 - style: double + shorthand-values: 0 zero-unit: 2 diff --git a/package.json b/package.json index 098037279..20e5719b7 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "react-slick": "0.9.2", "redux-thunk": "2.0.1", "routes-to-nginx-conf": "0.0.4", - "sass-lint": "1.3.2", + "sass-lint": "1.5.1", "sass-loader": "2.0.1", "scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master", "slick-carousel": "1.5.8", diff --git a/src/components/adminpanel/adminpanel.scss b/src/components/adminpanel/adminpanel.scss index 64ef709a0..6b8c36907 100644 --- a/src/components/adminpanel/adminpanel.scss +++ b/src/components/adminpanel/adminpanel.scss @@ -38,7 +38,7 @@ font-size: large; font-weight: 700; } - + dd { margin-left: 0; } @@ -51,21 +51,22 @@ margin: 0; list-style: none; - .button-row { - display: flex; - font-size: small; - align-items: center; - justify-content: space-between; + } + } + } - .button { - padding: .5rem 1rem; + .button-row { + display: flex; + font-size: small; + align-items: center; + justify-content: space-between; - &.inprogress { - background-color: $ui-dark-gray; - color: $type-gray; - } - } - } + .button { + padding: .5rem 1rem; + + &.inprogress { + background-color: $ui-dark-gray; + color: $type-gray; } } } diff --git a/src/components/banner/banner.scss b/src/components/banner/banner.scss index 2596df6c8..64a69d638 100644 --- a/src/components/banner/banner.scss +++ b/src/components/banner/banner.scss @@ -13,7 +13,8 @@ $navigation-height: 50px; text-align: center; line-height: $navigation-height; - &, a { + &, + a { color: $ui-white; } @@ -23,14 +24,14 @@ $navigation-height: 50px; .close { float: right; - margin-top: $navigation-height/4; - border-radius: $navigation-height/4; + margin-top: $navigation-height / 4; + border-radius: $navigation-height / 4; background-color: $box-shadow-gray; - width: $navigation-height/2; - height: $navigation-height/2; + width: $navigation-height / 2; + height: $navigation-height / 2; text-decoration: none; text-shadow: none; - line-height: $navigation-height/2; + line-height: $navigation-height / 2; color: $ui-white; font-weight: normal; } diff --git a/src/components/box/box.scss b/src/components/box/box.scss index 632c31a07..01310720f 100644 --- a/src/components/box/box.scss +++ b/src/components/box/box.scss @@ -11,6 +11,7 @@ $base-bg: $ui-white; //4 columns @media only screen and (max-width: $mobile - 1) { width: $cols4; + .box-header { h4 { font-size: .9rem; @@ -21,9 +22,10 @@ $base-bg: $ui-white; //6 columns @media only screen and (min-width: $mobile) and (max-width: $tablet - 1) { width: $cols6; + .box-header { h4 { - font-size: 1.0rem; + font-size: 1rem; } } } @@ -31,6 +33,7 @@ $base-bg: $ui-white; //8 columns @media only screen and (min-width: $tablet) and (max-width: $desktop - 1) { width: $cols8; + .box-header { h4 { font-size: 1.1rem; @@ -41,6 +44,7 @@ $base-bg: $ui-white; //12 columns @media only screen and (min-width: $desktop) { width: $cols12; + .box-header { h4 { font-size: 1.1rem; diff --git a/src/components/carousel/carousel.scss b/src/components/carousel/carousel.scss index dd2bac747..b8fbcdbf4 100644 --- a/src/components/carousel/carousel.scss +++ b/src/components/carousel/carousel.scss @@ -14,7 +14,7 @@ .slick-next, .slick-prev { - margin-top: -$icon-size/2; + margin-top: -$icon-size / 2; width: $icon-size; height: $icon-size; diff --git a/src/components/intro/intro.scss b/src/components/intro/intro.scss index 5406f8d54..d8fb1e43b 100644 --- a/src/components/intro/intro.scss +++ b/src/components/intro/intro.scss @@ -57,14 +57,6 @@ display: none; } - &:hover .costume-1 { - display: none; - } - - &:hover .costume-2 { - display: block; - } - .circle { display: block; top: 15px; @@ -98,52 +90,74 @@ } - &.sprite-1 .circle { - background-color: $splash-green; + &.sprite-1 { + .circle { + background-color: $splash-green; + } + + .text { + top: 60px; + left: 50px; + color: $splash-green; + } } - &.sprite-2 .circle { - background-color: $splash-pink; + &.sprite-2 { + .circle { + background-color: $splash-pink; + } + + .text { + top: 77px; + left: 50px; + color: $splash-pink; + } } - &.sprite-3 .circle { - background-color: $splash-blue; + &.sprite-3 { + .circle { + background-color: $splash-blue; + } + + .text { + top: 37px; + left: 45px; + color: $splash-blue; + } + + .subtext { + top: 63px; + left: 60px; + color: $ui-white; + } } - &:hover.sprite-1 .circle { - box-shadow: 0 0 10px 2px $splash-green; - } + &:hover { + .costume-1 { + display: none; + } - &:hover.sprite-2 .circle { - box-shadow: 0 0 10px 2px $splash-pink; - } + .costume-2 { + display: block; + } - &:hover.sprite-3 .circle { - box-shadow: 0 0 10px 2px $splash-blue; - } + &.sprite-1 { + .circle { + box-shadow: 0 0 10px 2px $splash-green; + } + } - &.sprite-1 .text { - top: 60px; - left: 50px; - color: $splash-green; - } + &.sprite-2 { + .circle { + box-shadow: 0 0 10px 2px $splash-pink; + } + } - &.sprite-2 .text { - top: 77px; - left: 50px; - color: $splash-pink; - } - - &.sprite-3 .text { - top: 37px; - left: 45px; - color: $splash-blue; - } - - &.sprite-3 .subtext { - top: 63px; - left: 60px; - color: $ui-white; + &.sprite-3 { + .circle { + box-shadow: 0 0 10px 2px $splash-blue; + } + } } } diff --git a/src/components/languagechooser/languagechooser.jsx b/src/components/languagechooser/languagechooser.jsx index 39d39887b..765f883ba 100644 --- a/src/components/languagechooser/languagechooser.jsx +++ b/src/components/languagechooser/languagechooser.jsx @@ -6,8 +6,6 @@ var jar = require('../../lib/jar.js'); var languages = require('../../../languages.json'); var Select = require('../forms/select.jsx'); -require('./languagechooser.scss'); - /** * Footer dropdown menu that allows one to change their language. */ diff --git a/src/components/languagechooser/languagechooser.scss b/src/components/languagechooser/languagechooser.scss deleted file mode 100644 index abe1a6d34..000000000 --- a/src/components/languagechooser/languagechooser.scss +++ /dev/null @@ -1,3 +0,0 @@ -.language-chooser { - -} diff --git a/src/components/login/login.scss b/src/components/login/login.scss index 75709bc3e..5f70c0b08 100644 --- a/src/components/login/login.scss +++ b/src/components/login/login.scss @@ -20,11 +20,12 @@ a { margin-top: 15px; + + &:hover { + background-color: transparent; + } } - a:hover { - background-color: transparent; - } .error { border: 1px solid $active-dark-gray; diff --git a/src/components/modal/modal.scss b/src/components/modal/modal.scss index 49503eaf4..fae33030d 100644 --- a/src/components/modal/modal.scss +++ b/src/components/modal/modal.scss @@ -1,6 +1,6 @@ @import "../../colors"; -&.ReactModal__Content { +.ReactModal__Content { iframe { border: 0; } @@ -11,10 +11,10 @@ position: absolute; top: 0; right: 0; - margin-top: -$modal-close-size/2; - margin-right: -$modal-close-size/2; + margin-top: -$modal-close-size / 2; + margin-right: -$modal-close-size / 2; border: 2px solid $ui-border; - border-radius: $modal-close-size/2; + border-radius: $modal-close-size / 2; background-color: $active-dark-gray; cursor: pointer; width: $modal-close-size; diff --git a/src/components/navigation/dropdown.scss b/src/components/navigation/dropdown.scss index 5b60414f9..78ef27a81 100644 --- a/src/components/navigation/dropdown.scss +++ b/src/components/navigation/dropdown.scss @@ -64,7 +64,7 @@ &:before { display: block; position: absolute; - top: -$arrow-border-width/2; + top: -$arrow-border-width / 2; right: 10%; transform: rotate(45deg); diff --git a/src/components/navigation/navigation.jsx b/src/components/navigation/navigation.jsx index 626dde015..f96ac2dbe 100644 --- a/src/components/navigation/navigation.jsx +++ b/src/components/navigation/navigation.jsx @@ -193,7 +193,7 @@ var Navigation = React.createClass({ 'logged-in': this.props.session.user }); var messageClasses = classNames({ - 'messageCount': true, + 'message-count': true, 'show': this.state.unreadMessageCount > 0 }); var formatMessage = this.props.intl.formatMessage; @@ -269,7 +269,7 @@ var Navigation = React.createClass({ ,
  • - + {this.props.session.user.username} diff --git a/src/components/navigation/navigation.scss b/src/components/navigation/navigation.scss index b2c11a71b..93f2e9714 100644 --- a/src/components/navigation/navigation.scss +++ b/src/components/navigation/navigation.scss @@ -51,72 +51,73 @@ vertical-align: bottom; } } + } - .logo { - margin-right: 10px; + .logo { + margin-right: 10px; - a { - display: block; - transition: .15s ease all; - margin: 0 6px 0 0; - border: 0; + a { + display: block; + transition: .15s ease all; + margin: 0 6px 0 0; + border: 0; - background-image: url("/images/logo_sm.png"); - background-repeat: no-repeat; - background-position: center center; - background-size: 95%; - width: 81px; - height: 50px; + background-image: url("/images/logo_sm.png"); + background-repeat: no-repeat; + background-position: center center; + background-size: 95%; + width: 81px; + height: 50px; - &:hover { - transition: .15s ease all; - background-size: 100%; - } + &:hover { + transition: .15s ease all; + background-size: 100%; } } + } - .link { - > a { - display: block; - padding: 17px 15px 0 15px; - height: 33px; - - text-decoration: none; - white-space: nowrap; - color: $type-white; - font-size: .85rem; - font-weight: bold; - } - - > a:hover { - background-color: $active-gray; - } - } - - .search { - margin: 0 20px; - border-right: 0; + .link { + > a { + display: block; + padding: 17px 15px 0 15px; + height: 33px; + + text-decoration: none; + white-space: nowrap; color: $type-white; - flex-grow: 3; + font-size: .85rem; + font-weight: bold; - .ie9 & { - width: 100%; - } - - form { - margin: 0; - } - - input { - display: inline-block; - margin-top: 5px; - outline: none; - border: 0; + &:hover { background-color: $active-gray; - height: 14px; } + } - input[type=submit] { + } + + .search { + margin: 0 20px; + border-right: 0; + color: $type-white; + flex-grow: 3; + + .ie9 & { + width: 100%; + } + + form { + margin: 0; + } + + input { + display: inline-block; + margin-top: 5px; + outline: none; + border: 0; + background-color: $active-gray; + height: 14px; + + [type=submit] { position: absolute; background-color: transparent; @@ -129,7 +130,7 @@ height: 40px; } - input[type=text] { + [type=text] { transition: .15s ease background-color; padding: 0; padding-right: 10px; @@ -148,122 +149,125 @@ transition: .15s ease background-color; background-color: $active-dark-gray; } - } - .ie9 input[type=text] { - width: 70px; + .ie9 & { + width: 70px; + } } } + } - .right { - float: right; - margin-left: auto; - align-self: flex-end; + .right { + float: right; + margin-left: auto; + align-self: flex-end; - .ie9 & { - float: none; - } + .ie9 & { + float: none; + } - a:hover { + a { + &:hover { background-color: $active-gray; } } + } - .messages, - .mystuff { - > a { - background-repeat: no-repeat; - background-position: center center; - background-size: 45%; - padding-right: 10px; - padding-left: 10px; - width: 30px; - overflow: hidden; - text-indent: 50px; - white-space: nowrap; - } + .messages, + .mystuff { + > a { + background-repeat: no-repeat; + background-position: center center; + background-size: 45%; + padding-right: 10px; + padding-left: 10px; + width: 30px; + overflow: hidden; + text-indent: 50px; + white-space: nowrap; - > a:hover { + &:hover { background-size: 50%; } } - .messages { - > a { - background-image: url("/images/nav-notifications.png"); - } + } - .messageCount { - display: none; - - &.show { - display: block; - position: absolute; - top: .5rem; - right: .25rem; - border-radius: 1rem; - background-color: $ui-orange; - padding: 0 .25rem; - text-indent: 0; - line-height: 1rem; - color: $type-white; - font-size: .7rem; - font-weight: bold; - } - } + .messages { + > a { + background-image: url("/images/nav-notifications.png"); } - .mystuff { - > a { - background-image: url("/images/mystuff.png"); - } - } + .message-count { + display: none; - .login-dropdown { - width: 200px; - } - - .account-nav { - .userInfo { - padding-top: 14px; - max-width: 260px; - } - - > a { - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - font-size: .8125rem; - font-weight: normal; - - .avatar { - margin-right: 10px; - border-radius: 3px; - width: 24px; - height: 24px; - vertical-align: middle; - } - - &:after { - display: inline-block; - margin-left: 8px; - - background-image: url("/images/dropdown.png"); - background-repeat: no-repeat; - background-position: center center; - background-size: 50%; - width: 20px; - height: 20px; - vertical-align: middle; - content: " "; - } - } - - .dropdown { - padding: 0; - padding-top: 5px; - width: 100%; + &.show { + display: block; + position: absolute; + top: .5rem; + right: .25rem; + border-radius: 1rem; + background-color: $ui-orange; + padding: 0 .25rem; + text-indent: 0; + line-height: 1rem; + color: $type-white; + font-size: .7rem; + font-weight: bold; } } } + + .mystuff { + > a { + background-image: url("/images/mystuff.png"); + } + } + + .login-dropdown { + width: 200px; + } + + .account-nav { + .user-info { + padding-top: 14px; + max-width: 260px; + } + + > a { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + font-size: .8125rem; + font-weight: normal; + + .avatar { + margin-right: 10px; + border-radius: 3px; + width: 24px; + height: 24px; + vertical-align: middle; + } + + &:after { + display: inline-block; + margin-left: 8px; + + background-image: url("/images/dropdown.png"); + background-repeat: no-repeat; + background-position: center center; + background-size: 50%; + width: 20px; + height: 20px; + vertical-align: middle; + content: " "; + } + } + + .dropdown { + padding: 0; + padding-top: 5px; + width: 100%; + } + } } diff --git a/src/components/news/news.scss b/src/components/news/news.scss index e58bee698..bb74aae6d 100644 --- a/src/components/news/news.scss +++ b/src/components/news/news.scss @@ -46,10 +46,10 @@ color: $type-gray; font-size: .85rem; } - } - li:nth-child(even) { - border-top: 1px solid $ui-border; - border-bottom: 1px solid $ui-border; + &:nth-child(even) { + border-top: 1px solid $ui-border; + border-bottom: 1px solid $ui-border; + } } } diff --git a/src/components/spinner/spinner.scss b/src/components/spinner/spinner.scss index f021901d6..e4fd45305 100644 --- a/src/components/spinner/spinner.scss +++ b/src/components/spinner/spinner.scss @@ -21,27 +21,33 @@ width: 15%; height: 15%; content: ""; - -webkit-animation: circleFadeDelay 1.2s infinite ease-in-out both; } } @for $i from 1 through 12 { $rotation: 30deg * ($i - 1); $delay: -1.3s + $i * .1; + .circle#{$i} { transform: rotate($rotation); - -ms-transform: rotate($rotation); - -webkit-transform: rotate($rotation); - } - .circle#{$i}:before { - animation-delay: $delay; - -webkit-animation-delay: $delay; + + &:before { + animation-delay: $delay; + } } + } } @keyframes circleFadeDelay { - 0%, 39%, 100% { opacity: 0; } - 40% { opacity: 1; } + 0%, + 39%, + 100% { + opacity: 0; + } + + 40% { + opacity: 1; + } } diff --git a/src/components/subnavigation/subnavigation.scss b/src/components/subnavigation/subnavigation.scss index 0f951cd4e..76248f1d6 100644 --- a/src/components/subnavigation/subnavigation.scss +++ b/src/components/subnavigation/subnavigation.scss @@ -37,7 +37,7 @@ &.description { /* clear styling for info element */ - border: none; + border: 0; border-radius: none; text-decoration: none; @@ -47,7 +47,7 @@ } &:active { - border: none; + border: 0; box-shadow: none; background-color: transparent; } diff --git a/src/components/thumbnail/thumbnail.scss b/src/components/thumbnail/thumbnail.scss index ffaea2754..29498673d 100644 --- a/src/components/thumbnail/thumbnail.scss +++ b/src/components/thumbnail/thumbnail.scss @@ -3,11 +3,11 @@ .thumbnail { .thumbnail-image { display: block; - } - .thumbnail-image img { - margin-bottom: 2px; - border: 1px solid $ui-border; + img { + margin-bottom: 2px; + border: 1px solid $ui-border; + } } $extras: ".thumbnail-creator, .thumbnail-loves, .thumbnail-remixes"; @@ -26,7 +26,7 @@ .thumbnail-title { margin-bottom: 1px; - font-size: .9230em; + font-size: .923em; font-weight: 800; a { @@ -58,12 +58,16 @@ } } - .thumbnail-loves:before { - background-image: url("/svgs/love/love_type-gray.svg"); + .thumbnail-loves { + &:before { + background-image: url("/svgs/love/love_type-gray.svg"); + } } - .thumbnail-remixes:before { - background-image: url("/svgs/remix/remix_type-gray.svg"); + .thumbnail-remixes { + &:before { + background-image: url("/svgs/remix/remix_type-gray.svg"); + } } &.project { diff --git a/src/components/welcome/welcome.scss b/src/components/welcome/welcome.scss index a0e3437a5..a2404d7e7 100644 --- a/src/components/welcome/welcome.scss +++ b/src/components/welcome/welcome.scss @@ -4,6 +4,7 @@ .box-content { padding: 0; } + .welcome-col { display: inline-block; margin: 10px 15px; @@ -33,26 +34,32 @@ height: 10px; content: ""; } + &.blue { #{$color-bars} { background-color: $splash-blue; } + a { color: $splash-blue; } } + &.green { #{$color-bars} { background-color: $splash-green; } + a { color: $splash-green; } } + &.pink { #{$color-bars} { background-color: $splash-pink; } + a { color: $splash-pink; } diff --git a/src/main.scss b/src/main.scss index 64dd346bb..ed458a7a8 100644 --- a/src/main.scss +++ b/src/main.scss @@ -32,29 +32,31 @@ h1 { h4 { line-height: 1.1rem; - font-size: 1.0rem; + font-size: 1rem; } -p.legal { - font-size: .8rem; -} - -/* Links */ p { + &.legal { + font-size: .8rem; + } + a { white-space: nowrap; } } -a:link, -a:visited, -a:active { - text-decoration: none; - color: $link-blue; -} +/* Links */ +a { + &:link, + &:visited, + &:active { + text-decoration: none; + color: $link-blue; + } -a:hover { - text-decoration: underline; + &:hover { + text-decoration: underline; + } } /* Classes */ diff --git a/src/views/about/about.scss b/src/views/about/about.scss index d6008f3ec..1fe01fa41 100644 --- a/src/views/about/about.scss +++ b/src/views/about/about.scss @@ -60,7 +60,8 @@ width: calc(384px + 5px + 5px); } - img, iframe { + img, + iframe { display: block; border: 1px solid $ui-gray; padding: 5px; diff --git a/src/views/credits/credits.scss b/src/views/credits/credits.scss index 11dc0ded7..2a8b57ff5 100644 --- a/src/views/credits/credits.scss +++ b/src/views/credits/credits.scss @@ -3,11 +3,12 @@ #view { p { line-height: 1.5rem; + a { word-wrap: break-word; /* Overrides: https://github.com/LLK/scratch-www/blob/develop/src/main.scss#L43-L47 */ } } - + ul { display: flex; margin: 0; diff --git a/src/views/hoc/hoc.scss b/src/views/hoc/hoc.scss index d2c9ffe84..54a28ee6d 100644 --- a/src/views/hoc/hoc.scss +++ b/src/views/hoc/hoc.scss @@ -61,7 +61,7 @@ $base-bg: $ui-white; } } } - + .card-deck { display: inline-flex; justify-content: center; From d0ff2660bde642eb5197e1e8f038f6ac4d4165b8 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 24 Mar 2016 11:29:04 -0400 Subject: [PATCH 08/10] Don't configure sentry unless there's a DSN --- server/template.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/template.html b/server/template.html index 370edecd2..d68541291 100644 --- a/server/template.html +++ b/server/template.html @@ -55,7 +55,9 @@ From dce35a4beed837ae76afbf56f95446460851baef Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 24 Mar 2016 11:29:26 -0400 Subject: [PATCH 09/10] Add keys to children where necessary --- src/components/login/login.jsx | 14 +++++++------- src/components/navigation/navigation.jsx | 8 +++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/login/login.jsx b/src/components/login/login.jsx index 4dac048db..d12ce2361 100644 --- a/src/components/login/login.jsx +++ b/src/components/login/login.jsx @@ -40,30 +40,30 @@ var Login = React.createClass({ return (
    -