From 6a4462f196b5b840f77cce4ae9a7a7e6e266df32 Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Mon, 7 Dec 2015 16:05:05 +0100 Subject: [PATCH 01/30] Plugin-browser-list: refactored to ES6 --- .../plugins/plugins-browser-list/index.jsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/client/my-sites/plugins/plugins-browser-list/index.jsx b/client/my-sites/plugins/plugins-browser-list/index.jsx index e1b85b9967291..7e73991d2927e 100644 --- a/client/my-sites/plugins/plugins-browser-list/index.jsx +++ b/client/my-sites/plugins/plugins-browser-list/index.jsx @@ -1,27 +1,27 @@ /** * External dependencies */ -var React = require( 'react' ); +import React from 'react' /** * Internal dependencies */ -var PluginBrowserItem = require( 'my-sites/plugins/plugins-browser-item' ), - Gridicon = require( 'components/gridicon' ); +import PluginBrowserItem from 'my-sites/plugins/plugins-browser-item' +import Gridicon from 'components/gridicon' +import SectionHeader from 'components/section-header' -module.exports = React.createClass( { +export default React.createClass( { displayName: 'PluginsBrowserList', _DEFAULT_PLACEHOLDER_NUMBER: 6, - getPluginsViewList: function() { - var pluginsViewsList, - emptyCounter = 0; + getPluginsViewList() { + let emptyCounter = 0; - pluginsViewsList = this.props.plugins.map( function( plugin, n ) { + let pluginsViewsList = this.props.plugins.map( ( plugin, n ) => { return ; - }, this ); + } ); if ( this.props.showPlaceholders ) { pluginsViewsList = pluginsViewsList.concat( this.getPlaceholdersViews() ); @@ -39,13 +39,13 @@ module.exports = React.createClass( { return pluginsViewsList; }, - getPlaceholdersViews: function() { - return Array.apply( null, Array( this.props.size || this._DEFAULT_PLACEHOLDER_NUMBER ) ).map( function( item, i ) { + getPlaceholdersViews() { + return Array.apply( null, Array( this.props.size || this._DEFAULT_PLACEHOLDER_NUMBER ) ).map( ( item, i ) => { return ; } ); }, - getViews: function() { + getViews() { if ( this.props.plugins.length ) { return this.getPluginsViewList(); } else if ( this.props.showPlaceholders ) { @@ -53,7 +53,7 @@ module.exports = React.createClass( { } }, - getLink: function() { + getLink() { if ( this.props.expandedListLink ) { return { this.translate( 'See All' ) } @@ -62,7 +62,7 @@ module.exports = React.createClass( { } }, - render: function() { + render() { return (
From 99d6d57c8c6f3582a7a3350140935522edae30d8 Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Mon, 7 Dec 2015 16:08:51 +0100 Subject: [PATCH 02/30] Plugins-Browser-list: Apply section header --- client/my-sites/plugins/plugins-browser-list/index.jsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/client/my-sites/plugins/plugins-browser-list/index.jsx b/client/my-sites/plugins/plugins-browser-list/index.jsx index 7e73991d2927e..5b435e48dc51d 100644 --- a/client/my-sites/plugins/plugins-browser-list/index.jsx +++ b/client/my-sites/plugins/plugins-browser-list/index.jsx @@ -9,6 +9,7 @@ import React from 'react' import PluginBrowserItem from 'my-sites/plugins/plugins-browser-item' import Gridicon from 'components/gridicon' import SectionHeader from 'components/section-header' +import SectionHeaderButton from 'components/section-header/button' export default React.createClass( { @@ -65,12 +66,9 @@ export default React.createClass( { render() { return (
-
-

- { this.props.title } -

+ { this.getLink() } -
+
{ this.getViews() }
From abe9eb88c2c1674f4f53285256cd355627dbe918 Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Mon, 7 Dec 2015 16:50:03 +0100 Subject: [PATCH 03/30] Plugins-browser: Use Card for plugin browser lists --- client/my-sites/plugins/plugins-browser-list/index.jsx | 6 +++--- client/my-sites/plugins/plugins-browser-list/style.scss | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/my-sites/plugins/plugins-browser-list/index.jsx b/client/my-sites/plugins/plugins-browser-list/index.jsx index 5b435e48dc51d..39797d3bcac4a 100644 --- a/client/my-sites/plugins/plugins-browser-list/index.jsx +++ b/client/my-sites/plugins/plugins-browser-list/index.jsx @@ -7,9 +7,9 @@ import React from 'react' * Internal dependencies */ import PluginBrowserItem from 'my-sites/plugins/plugins-browser-item' +import Card from 'components/card' import Gridicon from 'components/gridicon' import SectionHeader from 'components/section-header' -import SectionHeaderButton from 'components/section-header/button' export default React.createClass( { @@ -69,9 +69,9 @@ export default React.createClass( { { this.getLink() } -
+ { this.getViews() } -
+
); } diff --git a/client/my-sites/plugins/plugins-browser-list/style.scss b/client/my-sites/plugins/plugins-browser-list/style.scss index b1dfd3b4f908c..7e62ac0f879ce 100644 --- a/client/my-sites/plugins/plugins-browser-list/style.scss +++ b/client/my-sites/plugins/plugins-browser-list/style.scss @@ -44,4 +44,5 @@ .plugins-browser-list__elements { min-height: 50px; overflow: hidden; // lazy clearfix + padding: 0; } From 2957c665fce92f938b413516745fec33be2e08dd Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Mon, 7 Dec 2015 17:27:34 +0100 Subject: [PATCH 04/30] Plugins-browser: Fix 'see all' button padding --- client/my-sites/plugins/plugins-browser-list/style.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/my-sites/plugins/plugins-browser-list/style.scss b/client/my-sites/plugins/plugins-browser-list/style.scss index 7e62ac0f879ce..0215ff9b5155d 100644 --- a/client/my-sites/plugins/plugins-browser-list/style.scss +++ b/client/my-sites/plugins/plugins-browser-list/style.scss @@ -17,7 +17,7 @@ .button.plugins-browser-list__select-all, .plugins-browser-list__title { display: inline-block; - padding: 6px 16px 7px; + padding: 6px 0px 7px; color: $gray; font-size: 11px; line-height: 1.6; @@ -38,7 +38,6 @@ .button.plugins-browser-list__select-all { float: right; - padding-right: 16px; } .plugins-browser-list__elements { From 3c571fecc5e6964149945ea00dfb6b02e686e43c Mon Sep 17 00:00:00 2001 From: Eric Binnion Date: Mon, 7 Dec 2015 17:14:58 -0600 Subject: [PATCH 05/30] Me: Fixes logic for gated security checkup nav tab --- client/me/security-section-nav/index.jsx | 49 +++++++++++++----------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/client/me/security-section-nav/index.jsx b/client/me/security-section-nav/index.jsx index 50ccad3d55c0f..7ba335831f6d4 100644 --- a/client/me/security-section-nav/index.jsx +++ b/client/me/security-section-nav/index.jsx @@ -18,27 +18,30 @@ module.exports = React.createClass( { path: React.PropTypes.string.isRequired }, - getDefaultProps: function() { - return { - tabs: [ - { - title: i18n.translate( 'Password', { textOnly: true } ), - path: '/me/security', - }, - { - title: i18n.translate( 'Two-Step Authentication', { textOnly: true } ), - path: '/me/security/two-step', - }, - { - title: i18n.translate( 'Connected Applications', { textOnly: true } ), - path: '/me/security/connected-applications', - }, - config.isEnabled( 'me/security/checkup' ) ? { - title: i18n.translate( 'Checkup', { textOnly: true } ), - path: '/me/security/checkup', - } : false - ] - }; + getNavtabs: function() { + var tabs = [ + { + title: i18n.translate( 'Password', { textOnly: true } ), + path: '/me/security', + }, + { + title: i18n.translate( 'Two-Step Authentication', { textOnly: true } ), + path: '/me/security/two-step', + }, + { + title: i18n.translate( 'Connected Applications', { textOnly: true } ), + path: '/me/security/connected-applications', + } + ]; + + if ( config.isEnabled( 'me/security/checkup' ) ) { + tabs.push( { + title: i18n.translate( 'Checkup', { textOnly: true } ), + path: '/me/security/checkup', + } ); + } + + return tabs; }, getFilteredPath: function() { @@ -48,7 +51,7 @@ module.exports = React.createClass( { getSelectedText: function() { var text = '', - found = find( this.props.tabs, function( tab ) { + found = find( this.getNavtabs(), function( tab ) { return this.getFilteredPath() === tab.path; }, this ); @@ -67,7 +70,7 @@ module.exports = React.createClass( { return ( - { this.props.tabs.map( function( tab ) { + { this.getNavtabs().map( function( tab ) { return ( Date: Wed, 9 Dec 2015 17:23:04 +0100 Subject: [PATCH 06/30] Section header: Vertical align label --- client/components/section-header/style.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/components/section-header/style.scss b/client/components/section-header/style.scss index 58738369b6725..c7616f8db8602 100644 --- a/client/components/section-header/style.scss +++ b/client/components/section-header/style.scss @@ -10,10 +10,13 @@ } .section-header__label { + display: flex; + align-items: center; flex-grow: 1; line-height: 28px; position: relative; + &:before { @include long-content-fade( $color : $white ); } From 969fd8bf83b55d85dd24f6fd418d04a650aa67b2 Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Wed, 9 Dec 2015 12:14:40 +0100 Subject: [PATCH 07/30] Framework: Check for gridicon sizes on commit --- bin/gridiconFormatChecker | 42 +++++++++++++++++++++++++++++++++++++++ bin/pre-commit | 9 +++++++++ 2 files changed, 51 insertions(+) create mode 100755 bin/gridiconFormatChecker diff --git a/bin/gridiconFormatChecker b/bin/gridiconFormatChecker new file mode 100755 index 0000000000000..4b7bcfd7031d0 --- /dev/null +++ b/bin/gridiconFormatChecker @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +var fs = require( 'fs' ); +var readline = require( 'readline' ); +var validIconSizes = [ 12, 18, 24, 36, 48, 54, 72 ]; + +var file = process.argv[ 2 ]; + +function checkGridicons( filename ) { + var lineNumber = 0, + result = ''; + readline.createInterface( { + input: fs.createReadStream( filename ), + terminal: false + } ).on( 'line', function( line ) { + var gridIconProps, onlyAttrs, size; + + if ( line.indexOf( '= 0 ) { + gridIconProps = line.split( '' )[ 0 ].split( ' ' ).join( '' ); + if ( onlyAttrs.indexOf( 'size={' ) >= 0 ) { + size = onlyAttrs.split( 'size={' )[ 1 ].split( '}' )[ 0 ]; + if ( !isNaN( size ) && validIconSizes.indexOf( +size ) < 0 ) { + result += '\033[31mNon-standard gridicon size ( ' + size + 'px ) detected in ' + filename + ' line ' + lineNumber + '\n'; + } + } + } + } + lineNumber++; + } ).on( 'close', function() { + if ( result !== '' ) { + console.error( result ); + console.log( '\033[mValid gridiconsizes are ' + validIconSizes.join( 'px, ' ) + 'px\n' ); + process.exit( 1 ); + } else { + process.exit( 0 ); + } + } ); +} + +checkGridicons( file ); diff --git a/bin/pre-commit b/bin/pre-commit index e1a5531fd9fa9..ae2d0d77f2256 100755 --- a/bin/pre-commit +++ b/bin/pre-commit @@ -38,6 +38,15 @@ done echo "\neslint validation complete\n" +for file in ${files}; do + ./bin/gridiconFormatChecker ${file} + if [ $? -ne 0 ]; then + echo "\033[31mGridicon Format Check Failed: \033[0m${file}\n" + pass=false + fi +done + + if ! $pass; then echo "\033[41mCOMMIT FAILED:\033[0m Your commit contains files that should pass validation tests but do not. Please fix the errors and try again.\n" exit 1 From 718fde238b400e60e09ccd6f3e663578272fda74 Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Thu, 10 Dec 2015 10:23:40 +0100 Subject: [PATCH 08/30] Gridicons: adds 'nonStandard' prop so the precommit checker can skip certain gridicons --- bin/gridiconFormatChecker | 12 +++++++++--- shared/components/gridicon/README.md | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/bin/gridiconFormatChecker b/bin/gridiconFormatChecker index 4b7bcfd7031d0..08d4888f6012d 100755 --- a/bin/gridiconFormatChecker +++ b/bin/gridiconFormatChecker @@ -13,16 +13,22 @@ function checkGridicons( filename ) { input: fs.createReadStream( filename ), terminal: false } ).on( 'line', function( line ) { - var gridIconProps, onlyAttrs, size; + var gridIconProps, onlyAttrs, size, isNonStandard = false; if ( line.indexOf( '= 0 ) { gridIconProps = line.split( '' )[ 0 ].split( ' ' ).join( '' ); + isNonStandard = onlyAttrs.indexOf( 'nonStandard' ) >= 0; if ( onlyAttrs.indexOf( 'size={' ) >= 0 ) { size = onlyAttrs.split( 'size={' )[ 1 ].split( '}' )[ 0 ]; - if ( !isNaN( size ) && validIconSizes.indexOf( +size ) < 0 ) { - result += '\033[31mNon-standard gridicon size ( ' + size + 'px ) detected in ' + filename + ' line ' + lineNumber + '\n'; + if ( !isNaN( size ) ) { + if( !isNonStandard && validIconSizes.indexOf( +size ) < 0 ) { + result += '\033[31mNon-standard gridicon size ( ' + size + 'px ) detected in ' + filename + ' line ' + lineNumber + '\n'; + } + if( isNonStandard && validIconSizes.indexOf( +size ) >= 0 ) { + result += '\033[33mStandard size gridicon ( ' + size + 'px ) marked as non-standard... are you sure that is ok? in ' + filename + ' line ' + lineNumber + '\n'; + } } } } diff --git a/shared/components/gridicon/README.md b/shared/components/gridicon/README.md index 1c0f9373d96e4..05b171b91355e 100644 --- a/shared/components/gridicon/README.md +++ b/shared/components/gridicon/README.md @@ -18,3 +18,4 @@ render: function() { * `icon`: String - the icon name. * `size`: Number - (default: 24) set the size of the icon. * `onClick`: Function - (optional) if you need a click callback. +* `nonStandard`: Boolean - (optional) A semantic prop to indicate (to our automatic tools and other developers) that this gridicon is not using one of the standard sizes on purpose. It must be combined with an additional comment explaining why the odd size is necesary. From e8ae82a1031f4d4e3351db52e8244f1503a16298 Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Thu, 10 Dec 2015 16:54:02 +0100 Subject: [PATCH 09/30] Gridicons: Support for multiline & several gridicons in the same file --- bin/gridiconFormatChecker | 63 ++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/bin/gridiconFormatChecker b/bin/gridiconFormatChecker index 08d4888f6012d..0ebdf91098a42 100755 --- a/bin/gridiconFormatChecker +++ b/bin/gridiconFormatChecker @@ -1,28 +1,33 @@ #!/usr/bin/env node var fs = require( 'fs' ); -var readline = require( 'readline' ); var validIconSizes = [ 12, 18, 24, 36, 48, 54, 72 ]; -var file = process.argv[ 2 ]; +var filename = process.argv[ 2 ]; -function checkGridicons( filename ) { - var lineNumber = 0, - result = ''; - readline.createInterface( { - input: fs.createReadStream( filename ), - terminal: false - } ).on( 'line', function( line ) { - var gridIconProps, onlyAttrs, size, isNonStandard = false; +fs.readFile( filename, 'utf8', function ( err, data ) { + var result = '', + splittedCode, + lineNumber = 1; + if ( err ) { + console.log(err); + process.exit( 1 ); + } + data = data.toLowerCase(); + splittedCode = data.split( '= 0 ) { - gridIconProps = line.split( '' )[ 0 ].split( ' ' ).join( '' ); - isNonStandard = onlyAttrs.indexOf( 'nonStandard' ) >= 0; - if ( onlyAttrs.indexOf( 'size={' ) >= 0 ) { - size = onlyAttrs.split( 'size={' )[ 1 ].split( '}' )[ 0 ]; + if ( splittedCode.length > 1 ) { + // There are gridicon instances in this file. + splittedCode.forEach( function( chunk ) { + var gridiconAttrs, isNonStandard, size; + if( chunk ) { + // we discard all the code after the tag closing... we are only interested in the props of the gridicon. + gridiconAttrs = chunk.split( '>' )[ 0 ]; + isNonStandard = gridiconAttrs.indexOf( 'nonstandard' ) >= 0; + if ( gridiconAttrs.indexOf( 'size={' ) >= 0 ) { + size = gridiconAttrs.split( 'size={' )[ 1 ].split( '}' )[ 0 ]; if ( !isNaN( size ) ) { + // We only can check if the size is standard if it is a number. If not (variables), we have no way of knowing if it's fine or not if( !isNonStandard && validIconSizes.indexOf( +size ) < 0 ) { result += '\033[31mNon-standard gridicon size ( ' + size + 'px ) detected in ' + filename + ' line ' + lineNumber + '\n'; } @@ -31,18 +36,16 @@ function checkGridicons( filename ) { } } } + lineNumber += chunk.split('\n').length - 1; } - } - lineNumber++; - } ).on( 'close', function() { - if ( result !== '' ) { - console.error( result ); - console.log( '\033[mValid gridiconsizes are ' + validIconSizes.join( 'px, ' ) + 'px\n' ); - process.exit( 1 ); - } else { - process.exit( 0 ); - } - } ); -} + } ); + } -checkGridicons( file ); + if ( result !== '' ) { + console.error( result ); + console.log( '\033[m=== Valid gridiconsizes are ' + validIconSizes.join( 'px, ' ) + 'px ===\n' ); + process.exit( 1 ); + } else { + process.exit( 0 ); + } +} ); From 9edc979e9b7ff72ea48991a9043bebcf73e4e4d0 Mon Sep 17 00:00:00 2001 From: johnHackworth Date: Thu, 10 Dec 2015 22:07:51 +0100 Subject: [PATCH 10/30] Gridicons: change the nonStandard property name to reflect it only makes reference to size --- bin/gridiconFormatChecker | 2 +- shared/components/gridicon/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/gridiconFormatChecker b/bin/gridiconFormatChecker index 0ebdf91098a42..08fc64dd74608 100755 --- a/bin/gridiconFormatChecker +++ b/bin/gridiconFormatChecker @@ -23,7 +23,7 @@ fs.readFile( filename, 'utf8', function ( err, data ) { if( chunk ) { // we discard all the code after the tag closing... we are only interested in the props of the gridicon. gridiconAttrs = chunk.split( '>' )[ 0 ]; - isNonStandard = gridiconAttrs.indexOf( 'nonstandard' ) >= 0; + isNonStandard = gridiconAttrs.indexOf( 'nonstandardsize' ) >= 0; if ( gridiconAttrs.indexOf( 'size={' ) >= 0 ) { size = gridiconAttrs.split( 'size={' )[ 1 ].split( '}' )[ 0 ]; if ( !isNaN( size ) ) { diff --git a/shared/components/gridicon/README.md b/shared/components/gridicon/README.md index 05b171b91355e..18dc947b41662 100644 --- a/shared/components/gridicon/README.md +++ b/shared/components/gridicon/README.md @@ -18,4 +18,4 @@ render: function() { * `icon`: String - the icon name. * `size`: Number - (default: 24) set the size of the icon. * `onClick`: Function - (optional) if you need a click callback. -* `nonStandard`: Boolean - (optional) A semantic prop to indicate (to our automatic tools and other developers) that this gridicon is not using one of the standard sizes on purpose. It must be combined with an additional comment explaining why the odd size is necesary. +* `nonStandardSize`: Boolean - (optional) A semantic prop to indicate (to our automatic tools and other contributors) that this gridicon is not using one of the standard sizes on purpose. It must be combined with an additional comment explaining why the odd size is necesary. From dc75c3247cb66a6d96effaf9754ab122a4382d2c Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Thu, 10 Dec 2015 14:51:13 -0800 Subject: [PATCH 11/30] Section Header: move to a standard font size of 11px --- client/components/section-header/style.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/components/section-header/style.scss b/client/components/section-header/style.scss index c7616f8db8602..d149a5b5c3edf 100644 --- a/client/components/section-header/style.scss +++ b/client/components/section-header/style.scss @@ -36,13 +36,12 @@ .section-header__label, .section-header__button { color: $gray; - font-size: 12px; + font-size: 11px; text-transform: uppercase; } .section-header__button { background: none; - float: let; margin-right: 8px; padding: 2px 8px; From 6b8454cf100923a822df9c6f5dddd54a0478c990 Mon Sep 17 00:00:00 2001 From: artpi Date: Tue, 8 Dec 2015 22:17:38 +0100 Subject: [PATCH 12/30] Notices: connect Layouts to global redux store --- client/boot/index.js | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/client/boot/index.js b/client/boot/index.js index 22a432d256eda..40b939a1e97f4 100644 --- a/client/boot/index.js +++ b/client/boot/index.js @@ -11,6 +11,7 @@ var React = require( 'react' ), page = require( 'page' ), url = require( 'url' ), qs = require( 'querystring' ), + ReduxProvider = require( 'react-redux' ).Provider, injectTapEventPlugin = require( 'react-tap-event-plugin' ); /** @@ -78,9 +79,7 @@ function init() { } ); } -function setUpContext( layout ) { - var reduxStore = createReduxStore(); - +function setUpContext( layout, reduxStore ) { // Pass the layout so that it is available to all page handlers // and add query and hash objects onto context object page( '*', function( context, next ) { @@ -138,7 +137,7 @@ function loadDevModulesAndBoot() { } function boot() { - var layoutSection, layout, validSections = []; + var layoutSection, layout, reduxStore, validSections = []; init(); @@ -153,6 +152,7 @@ function boot() { } ); translatorJumpstart.init(); + reduxStore = createReduxStore(); if ( user.get() ) { // When logged in the analytics module requires user and superProps objects @@ -161,13 +161,17 @@ function boot() { // Create layout instance with current user prop Layout = require( 'layout' ); - layout = React.render( React.createElement( Layout, { - user: user, - sites: sites, - focus: layoutFocus, - nuxWelcome: nuxWelcome, - translatorInvitation: translatorInvitation - } ), document.getElementById( 'wpcom' ) ); + layout = React.render( + React.createElement( ReduxProvider, { store: reduxStore }, () => { + return React.createElement( Layout, { + user: user, + sites: sites, + focus: layoutFocus, + nuxWelcome: nuxWelcome, + translatorInvitation: translatorInvitation + } ) + } + ), document.getElementById( 'wpcom' ) ); } else { analytics.setSuperProps( superProps ); @@ -178,7 +182,9 @@ function boot() { } layout = React.render( - React.createElement( LoggedOutLayout ), + React.createElement( ReduxProvider, { store: reduxStore }, () => { + return React.createElement( LoggedOutLayout ) + } ), document.getElementById( 'wpcom' ) ); } @@ -193,7 +199,7 @@ function boot() { window.history.replaceState( null, document.title, window.location.pathname ); } - setUpContext( layout ); + setUpContext( layout, reduxStore ); page( '*', require( 'lib/route/normalize' ) ); From 9f2932cddd1d75d7694545f5412bb8357b0ca102 Mon Sep 17 00:00:00 2001 From: artpi Date: Tue, 8 Dec 2015 22:18:45 +0100 Subject: [PATCH 13/30] Notices: Add global-notices component --- client/notices/global-notices.jsx | 84 +++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 client/notices/global-notices.jsx diff --git a/client/notices/global-notices.jsx b/client/notices/global-notices.jsx new file mode 100644 index 0000000000000..8ff39e9ca8d15 --- /dev/null +++ b/client/notices/global-notices.jsx @@ -0,0 +1,84 @@ +/** + * External Dependencies + */ +import React from 'react'; +import classNames from 'classnames'; +import debugModule from 'debug'; +import { connect } from 'react-redux'; + +/** + * Internal Dependencies + */ +import Notice from 'components/notice'; +const debug = debugModule( 'calypso:global-notices' ); + +var NoticesList = React.createClass( { + + displayName: 'GlobalNotices', + propTypes: { + id: React.PropTypes.string, + forcePinned: React.PropTypes.bool + }, + + getInitialState() { + return { pinned: this.props.forcePinned }; + }, + + componentDidMount() { + if ( ! this.props.forcePinned ) { + window.addEventListener( 'scroll', this.updatePinnedState ); + } + }, + + componentDidUpdate( prevProps ) { + if ( this.props.forcePinned && ! prevProps.forcePinned ) { + window.removeEventListener( 'scroll', this.updatePinnedState ); + this.setState( { pinned: true } ); + } else if ( ! this.props.forcePinned && prevProps.forcePinned ) { + window.addEventListener( 'scroll', this.updatePinnedState ); + this.updatePinnedState(); + } + }, + + componentWillUnmount() { + window.removeEventListener( 'scroll', this.updatePinnedState ); + }, + + updatePinnedState() { + this.setState( { pinned: window.scrollY > 0 } ); + }, + + render() { + const noticesList = this.props.notices.map( function( notice, index ) { + return ( + + + ); + }, this ); + + if ( ! this.props.notices.length ) { + return null; + } + return ( +
+
+ { noticesList } +
+ { this.state.pinned && ! this.props.forcePinned + ?
+ : null } +
+ ); + } +} ); + +export default connect( + ( state ) => { + return { + notices: state.notices.items + }; + } +)( NoticesList ); From 1c90e2dca06da6a617ebe71deca2f18b13cd59c4 Mon Sep 17 00:00:00 2001 From: artpi Date: Tue, 8 Dec 2015 22:20:10 +0100 Subject: [PATCH 14/30] Notices: Inject global-notices component into layouts --- client/components/overlay/overlay.jsx | 2 ++ client/layout/index.jsx | 2 ++ client/layout/logged-out.jsx | 2 ++ 3 files changed, 6 insertions(+) diff --git a/client/components/overlay/overlay.jsx b/client/components/overlay/overlay.jsx index d428302d6d2c6..4ca732eadb57a 100644 --- a/client/components/overlay/overlay.jsx +++ b/client/components/overlay/overlay.jsx @@ -9,6 +9,7 @@ var React = require( 'react/addons' ), * Internal dependencies */ var Toolbar = require( './toolbar' ), + GlobalNotices = require( 'notices/global-notices' ), NoticesList = require( 'notices/notices-list' ), notices = require( 'notices' ), page = require( 'page' ), @@ -101,6 +102,7 @@ module.exports = React.createClass({
+ { this.props.children }
diff --git a/client/layout/index.jsx b/client/layout/index.jsx index 81b696f292f25..20c0dc2ffb03e 100644 --- a/client/layout/index.jsx +++ b/client/layout/index.jsx @@ -11,6 +11,7 @@ var React = require( 'react' ), */ var Masterbar = require( './masterbar' ), observe = require( 'lib/mixins/data-observe' ), + GlobalNotices = require( 'notices/global-notices' ), NoticesList = require( 'notices/notices-list' ), notices = require( 'notices' ), translator = require( 'lib/translator-jumpstart' ), @@ -111,6 +112,7 @@ module.exports = React.createClass( { +
diff --git a/client/layout/logged-out.jsx b/client/layout/logged-out.jsx index 3f4ba8df5cea6..83f6ad3c4f1ca 100644 --- a/client/layout/logged-out.jsx +++ b/client/layout/logged-out.jsx @@ -9,6 +9,7 @@ var React = require( 'react' ), */ var Masterbar = require( './masterbar' ), NoticesList = require( 'notices/notices-list' ), + GlobalNotices = require( 'notices/global-notices' ), notices = require( 'notices' ); module.exports = React.createClass( { @@ -32,6 +33,7 @@ module.exports = React.createClass( {
+
From fac06c20be2816595b0d8c6a073989329b5b352f Mon Sep 17 00:00:00 2001 From: artpi Date: Tue, 8 Dec 2015 22:24:43 +0100 Subject: [PATCH 15/30] Notices: Add proper reducers --- client/state/index.js | 4 +++- client/state/notices/action-types.js | 1 + client/state/notices/reducers.js | 32 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 client/state/notices/action-types.js create mode 100644 client/state/notices/reducers.js diff --git a/client/state/index.js b/client/state/index.js index 52cbab6b5a1f7..a7cd2bb067b4b 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -10,6 +10,7 @@ import { createStore, applyMiddleware, combineReducers } from 'redux'; import sharing from './sharing/reducer'; import sites from './sites/reducer'; import ui from './ui/reducer'; +import notices from './notices/reducers'; /** * Module variables @@ -17,7 +18,8 @@ import ui from './ui/reducer'; const reducer = combineReducers( { sharing, sites, - ui + ui, + notices } ); export function createReduxStore() { diff --git a/client/state/notices/action-types.js b/client/state/notices/action-types.js new file mode 100644 index 0000000000000..199dbda1437eb --- /dev/null +++ b/client/state/notices/action-types.js @@ -0,0 +1 @@ +export const NEW_NOTICE = 'NEW_NOTICE'; diff --git a/client/state/notices/reducers.js b/client/state/notices/reducers.js new file mode 100644 index 0000000000000..da6c0885d7945 --- /dev/null +++ b/client/state/notices/reducers.js @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { combineReducers } from 'redux'; + +/** + * Internal dependencies + */ +import { + NEW_NOTICE +} from './action-types'; + +/** + * Tracks all known site objects, indexed by site ID. + * + * @param {Object} state Current state + * @param {Object} action Action payload + * @return {Object} Updated state + */ +export function items( state = [], action ) { + switch ( action.type ) { + case NEW_NOTICE: + state = [ action, ...state ]; + break; + } + + return state; +} + +export default combineReducers( { + items +} ); From 7c91bf3847f64d30567efb49138f863555f15edf Mon Sep 17 00:00:00 2001 From: artpi Date: Wed, 9 Dec 2015 14:22:48 +0100 Subject: [PATCH 16/30] Notices: move global state to singleton --- client/boot/index.js | 8 ++++---- client/state/index.js | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/boot/index.js b/client/boot/index.js index 40b939a1e97f4..5e14b4fbbb2cb 100644 --- a/client/boot/index.js +++ b/client/boot/index.js @@ -38,7 +38,7 @@ var config = require( 'config' ), touchDetect = require( 'lib/touch-detect' ), accessibleFocus = require( 'lib/accessible-focus' ), TitleStore = require( 'lib/screen-title/store' ), - createReduxStore = require( 'state' ).createReduxStore, + reduxStore = require( 'state' ), // The following mixins require i18n content, so must be required after i18n is initialized Layout, LoggedOutLayout; @@ -79,7 +79,7 @@ function init() { } ); } -function setUpContext( layout, reduxStore ) { +function setUpContext( layout ) { // Pass the layout so that it is available to all page handlers // and add query and hash objects onto context object page( '*', function( context, next ) { @@ -137,7 +137,7 @@ function loadDevModulesAndBoot() { } function boot() { - var layoutSection, layout, reduxStore, validSections = []; + var layoutSection, layout, validSections = []; init(); @@ -152,7 +152,7 @@ function boot() { } ); translatorJumpstart.init(); - reduxStore = createReduxStore(); + window.redux = reduxStore; if ( user.get() ) { // When logged in the analytics module requires user and superProps objects diff --git a/client/state/index.js b/client/state/index.js index a7cd2bb067b4b..ca9aba4acfcaa 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -22,8 +22,8 @@ const reducer = combineReducers( { notices } ); -export function createReduxStore() { - return applyMiddleware( - thunkMiddleware - )( createStore )( reducer ); -}; +const store = applyMiddleware( + thunkMiddleware +)( createStore )( reducer ); + +export default store; From b29b6ec8e22e028e13123a61d6bfa4e6fecb9430 Mon Sep 17 00:00:00 2001 From: artpi Date: Wed, 9 Dec 2015 14:23:56 +0100 Subject: [PATCH 17/30] Notices: add actions for notices --- client/state/notices/actions.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 client/state/notices/actions.js diff --git a/client/state/notices/actions.js b/client/state/notices/actions.js new file mode 100644 index 0000000000000..6314908662a91 --- /dev/null +++ b/client/state/notices/actions.js @@ -0,0 +1,14 @@ +/** + * Internal dependencies + */ +import { + NEW_NOTICE +} from './action-types'; + +export function newNotice( status, text, options ) { + return { + type: NEW_NOTICE, + status: status, + text: text + }; +} From c91799a199a5b107994048c7cc0ff38a430e7bbe Mon Sep 17 00:00:00 2001 From: artpi Date: Wed, 9 Dec 2015 14:24:32 +0100 Subject: [PATCH 18/30] Notices: add global notices handler --- client/state/notices/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 client/state/notices/index.js diff --git a/client/state/notices/index.js b/client/state/notices/index.js new file mode 100644 index 0000000000000..bf37147e15c35 --- /dev/null +++ b/client/state/notices/index.js @@ -0,0 +1,10 @@ +import store from 'state'; +import { newNotice } from './actions'; + +export function success( text, options ) { + return store.dispatch( newNotice( 'is-success', text, options ) ); +} + +export function error( text, options ) { + return store.dispatch( newNotice( 'is-error', text, options ) ); +} From d6e57984625348da071af281b8231df28b2aa6c2 Mon Sep 17 00:00:00 2001 From: artpi Date: Wed, 9 Dec 2015 14:25:04 +0100 Subject: [PATCH 19/30] Notices: Use global notices handler to display notices --- client/me/notification-settings/index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/me/notification-settings/index.jsx b/client/me/notification-settings/index.jsx index 7068ddaa337b3..02d53920aa0b9 100644 --- a/client/me/notification-settings/index.jsx +++ b/client/me/notification-settings/index.jsx @@ -7,7 +7,7 @@ import React from 'react'; * Internal dependencies */ import observe from 'lib/mixins/data-observe'; -import notices from 'notices'; +import { success, error } from 'state/notices'; import Main from 'components/main'; import ReauthRequired from 'me/reauth-required'; import twoStepAuthorization from 'lib/two-step-authorization'; @@ -43,11 +43,11 @@ export default React.createClass( { const state = store.getStateFor( 'blogs' ); if ( state.error ) { - notices.error( this.translate( 'There was a problem saving your changes. Please, try again.' ) ); + error( this.translate( 'There was a problem saving your changes. Please, try again.' ) ); } if ( state.status === 'success' ) { - notices.success( this.translate( 'Settings saved successfully!' ) ); + success( this.translate( 'Settings saved successfully!' ) ); } this.setState( state ); From 8d1dbde809bdecdecfd70acdc05f4d7c33c0551d Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 16:36:49 +0100 Subject: [PATCH 20/30] Notices: Remove helper Global notices helper is no longer needed --- client/state/notices/index.js | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 client/state/notices/index.js diff --git a/client/state/notices/index.js b/client/state/notices/index.js deleted file mode 100644 index bf37147e15c35..0000000000000 --- a/client/state/notices/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import store from 'state'; -import { newNotice } from './actions'; - -export function success( text, options ) { - return store.dispatch( newNotice( 'is-success', text, options ) ); -} - -export function error( text, options ) { - return store.dispatch( newNotice( 'is-error', text, options ) ); -} From b81249ca3650835cfbca2bb7e4e4f6376b88ccfa Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 16:38:02 +0100 Subject: [PATCH 21/30] Notices: Connect notification settings component to react-redux --- client/me/controller.js | 21 ++++++++++++--------- client/me/notification-settings/index.jsx | 22 ++++++++++++++++++---- client/state/notices/actions.js | 10 +++++++++- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/client/me/controller.js b/client/me/controller.js index daa2e4a5d28e2..812db235a34d0 100644 --- a/client/me/controller.js +++ b/client/me/controller.js @@ -18,6 +18,7 @@ import purchasesController from './purchases/controller'; import userFactory from 'lib/user'; import userSettings from 'lib/user-settings'; import titleActions from 'lib/screen-title/actions'; +import { Provider } from 'react-redux'; const ANALYTICS_PAGE_TITLE = 'Me', devices = devicesFactory(), @@ -192,15 +193,17 @@ export default { analytics.pageView.record( basePath, ANALYTICS_PAGE_TITLE + ' > Notifications' ); React.render( - React.createElement( NotificationsComponent, - { - user: user, - userSettings: userSettings, - blogs: sites, - devices: devices, - path: context.path - } - ), + React.createElement( Provider, { store: context.reduxStore }, () => { + return React.createElement( NotificationsComponent, + { + user: user, + userSettings: userSettings, + blogs: sites, + devices: devices, + path: context.path + } + ) + } ), document.getElementById( 'primary' ) ); }, diff --git a/client/me/notification-settings/index.jsx b/client/me/notification-settings/index.jsx index 02d53920aa0b9..dae3f92c92e65 100644 --- a/client/me/notification-settings/index.jsx +++ b/client/me/notification-settings/index.jsx @@ -7,7 +7,7 @@ import React from 'react'; * Internal dependencies */ import observe from 'lib/mixins/data-observe'; -import { success, error } from 'state/notices'; +import { success, error } from 'state/notices/actions'; import Main from 'components/main'; import ReauthRequired from 'me/reauth-required'; import twoStepAuthorization from 'lib/two-step-authorization'; @@ -16,8 +16,9 @@ import Navigation from './navigation'; import BlogsSettings from './blogs-settings'; import store from 'lib/notification-settings-store'; import { fetchSettings, toggle, saveSettings } from 'lib/notification-settings-store/actions'; +import { connect } from 'react-redux'; -export default React.createClass( { +const NotificationSettings = React.createClass( { displayName: 'NotificationSettings', mixins: [ observe( 'sites', 'devices' ) ], @@ -43,11 +44,11 @@ export default React.createClass( { const state = store.getStateFor( 'blogs' ); if ( state.error ) { - error( this.translate( 'There was a problem saving your changes. Please, try again.' ) ); + this.props.errorNotice( this.translate( 'There was a problem saving your changes. Please, try again.' ) ); } if ( state.status === 'success' ) { - success( this.translate( 'Settings saved successfully!' ) ); + this.props.successNotice( this.translate( 'Settings saved successfully!' ) ); } this.setState( state ); @@ -74,3 +75,16 @@ export default React.createClass( { } } ); +export default connect( + () => { + return {} + }, + ( dispatch ) => { return { + successNotice: ( text, options ) => { + dispatch( success( text + ':)', options ) ); + }, + errorNotice: ( text, options ) => { + dispatch( error( text + ':)', options ) ); + } + } } +)( NotificationSettings ); diff --git a/client/state/notices/actions.js b/client/state/notices/actions.js index 6314908662a91..4aa502c1f4b6b 100644 --- a/client/state/notices/actions.js +++ b/client/state/notices/actions.js @@ -5,10 +5,18 @@ import { NEW_NOTICE } from './action-types'; -export function newNotice( status, text, options ) { +function newNotice( status, text, options ) { return { type: NEW_NOTICE, status: status, text: text }; } + +export function success( text, options ) { + return newNotice( 'is-success', text, options ); +} + +export function error( text, options ) { + return newNotice( 'is-error', text, options ); +} From faa3e1840cc3e67a9ccc2860f1f204bcb2ab1d9e Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 16:39:54 +0100 Subject: [PATCH 22/30] Revert "Notices: move global state to singleton" We dont want to have global state in singleton, we shuld connect via react-redux This reverts commit 04e33336752e9e3367398e0ac7ac2e998a2ed6ae. --- client/boot/index.js | 8 ++++---- client/state/index.js | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/boot/index.js b/client/boot/index.js index 5e14b4fbbb2cb..40b939a1e97f4 100644 --- a/client/boot/index.js +++ b/client/boot/index.js @@ -38,7 +38,7 @@ var config = require( 'config' ), touchDetect = require( 'lib/touch-detect' ), accessibleFocus = require( 'lib/accessible-focus' ), TitleStore = require( 'lib/screen-title/store' ), - reduxStore = require( 'state' ), + createReduxStore = require( 'state' ).createReduxStore, // The following mixins require i18n content, so must be required after i18n is initialized Layout, LoggedOutLayout; @@ -79,7 +79,7 @@ function init() { } ); } -function setUpContext( layout ) { +function setUpContext( layout, reduxStore ) { // Pass the layout so that it is available to all page handlers // and add query and hash objects onto context object page( '*', function( context, next ) { @@ -137,7 +137,7 @@ function loadDevModulesAndBoot() { } function boot() { - var layoutSection, layout, validSections = []; + var layoutSection, layout, reduxStore, validSections = []; init(); @@ -152,7 +152,7 @@ function boot() { } ); translatorJumpstart.init(); - window.redux = reduxStore; + reduxStore = createReduxStore(); if ( user.get() ) { // When logged in the analytics module requires user and superProps objects diff --git a/client/state/index.js b/client/state/index.js index ca9aba4acfcaa..a7cd2bb067b4b 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -22,8 +22,8 @@ const reducer = combineReducers( { notices } ); -const store = applyMiddleware( - thunkMiddleware -)( createStore )( reducer ); - -export default store; +export function createReduxStore() { + return applyMiddleware( + thunkMiddleware + )( createStore )( reducer ); +}; From f107d4702bed5f325ba94ad268a3481f162e541b Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 16:44:55 +0100 Subject: [PATCH 23/30] Notices: Proper formatting of tabs --- client/state/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/state/index.js b/client/state/index.js index a7cd2bb067b4b..e278602f2e7c0 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -19,7 +19,7 @@ const reducer = combineReducers( { sharing, sites, ui, - notices + notices } ); export function createReduxStore() { From 113f92069d8d5cbaccd1a299bd5c2dae8e848c45 Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 16:49:30 +0100 Subject: [PATCH 24/30] Notices: remove extra chars from actionCreator --- client/me/notification-settings/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/me/notification-settings/index.jsx b/client/me/notification-settings/index.jsx index dae3f92c92e65..eb9710d56d95e 100644 --- a/client/me/notification-settings/index.jsx +++ b/client/me/notification-settings/index.jsx @@ -81,10 +81,10 @@ export default connect( }, ( dispatch ) => { return { successNotice: ( text, options ) => { - dispatch( success( text + ':)', options ) ); + dispatch( success( text, options ) ); }, errorNotice: ( text, options ) => { - dispatch( error( text + ':)', options ) ); + dispatch( error( text, options ) ); } } } )( NotificationSettings ); From f07e6484bb2217e4a724a076e466870875b2e03b Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 17:39:24 +0100 Subject: [PATCH 25/30] Notices: rename reduxStore to store One of earlier commits renamed this --- client/me/controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/me/controller.js b/client/me/controller.js index 812db235a34d0..c0960713f5cc6 100644 --- a/client/me/controller.js +++ b/client/me/controller.js @@ -193,7 +193,7 @@ export default { analytics.pageView.record( basePath, ANALYTICS_PAGE_TITLE + ' > Notifications' ); React.render( - React.createElement( Provider, { store: context.reduxStore }, () => { + React.createElement( Provider, { store: context.store }, () => { return React.createElement( NotificationsComponent, { user: user, From e54af031ed91f87baac94d53205c49d859369237 Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 17:54:05 +0100 Subject: [PATCH 26/30] Notices: clean up GlobalNotices minor problems --- client/notices/global-notices.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/notices/global-notices.jsx b/client/notices/global-notices.jsx index 8ff39e9ca8d15..46a623c8402ac 100644 --- a/client/notices/global-notices.jsx +++ b/client/notices/global-notices.jsx @@ -12,9 +12,8 @@ import { connect } from 'react-redux'; import Notice from 'components/notice'; const debug = debugModule( 'calypso:global-notices' ); -var NoticesList = React.createClass( { +const GlobalNotices = React.createClass( { - displayName: 'GlobalNotices', propTypes: { id: React.PropTypes.string, forcePinned: React.PropTypes.bool @@ -81,4 +80,4 @@ export default connect( notices: state.notices.items }; } -)( NoticesList ); +)( GlobalNotices ); From 8f3d3b3ecac07a73a592b8ffd7a5dcb860ad1d5e Mon Sep 17 00:00:00 2001 From: artpi Date: Thu, 10 Dec 2015 19:58:39 +0100 Subject: [PATCH 27/30] Notices: Clean up connected components in boot Now the duplication is gone --- client/boot/index.js | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/client/boot/index.js b/client/boot/index.js index 40b939a1e97f4..398a1b5832b21 100644 --- a/client/boot/index.js +++ b/client/boot/index.js @@ -137,7 +137,7 @@ function loadDevModulesAndBoot() { } function boot() { - var layoutSection, layout, reduxStore, validSections = []; + var layoutSection, layout, layoutComponentCreator, reduxStore, validSections = []; init(); @@ -161,17 +161,14 @@ function boot() { // Create layout instance with current user prop Layout = require( 'layout' ); - layout = React.render( - React.createElement( ReduxProvider, { store: reduxStore }, () => { - return React.createElement( Layout, { - user: user, - sites: sites, - focus: layoutFocus, - nuxWelcome: nuxWelcome, - translatorInvitation: translatorInvitation - } ) - } - ), document.getElementById( 'wpcom' ) ); + + layoutComponentCreator = () => React.createElement( Layout, { + user: user, + sites: sites, + focus: layoutFocus, + nuxWelcome: nuxWelcome, + translatorInvitation: translatorInvitation + } ); } else { analytics.setSuperProps( superProps ); @@ -181,14 +178,14 @@ function boot() { LoggedOutLayout = require( 'layout/logged-out' ); } - layout = React.render( - React.createElement( ReduxProvider, { store: reduxStore }, () => { - return React.createElement( LoggedOutLayout ) - } ), - document.getElementById( 'wpcom' ) - ); + layoutComponentCreator = () => React.createElement( LoggedOutLayout ); } + layout = React.render( + React.createElement( ReduxProvider, { store: reduxStore }, layoutComponentCreator ), + document.getElementById( 'wpcom' ) + ); + debug( 'Main layout rendered.' ); // If `?sb` or `?sp` are present on the path set the focus of layout From 5b50b05ad667f1ab5147a9c6e8c020ae657111fa Mon Sep 17 00:00:00 2001 From: artpi Date: Fri, 11 Dec 2015 11:33:06 +0100 Subject: [PATCH 28/30] Notices: Add dismissing of notice --- client/notices/global-notices.jsx | 10 +++++++++- client/state/notices/action-types.js | 1 + client/state/notices/actions.js | 13 ++++++++++++- client/state/notices/reducers.js | 7 ++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/client/notices/global-notices.jsx b/client/notices/global-notices.jsx index 46a623c8402ac..c1506eb7b3f11 100644 --- a/client/notices/global-notices.jsx +++ b/client/notices/global-notices.jsx @@ -5,6 +5,7 @@ import React from 'react'; import classNames from 'classnames'; import debugModule from 'debug'; import { connect } from 'react-redux'; +import { removeNotice } from 'state/notices/actions' /** * Internal Dependencies @@ -53,6 +54,8 @@ const GlobalNotices = React.createClass( { ); @@ -79,5 +82,10 @@ export default connect( return { notices: state.notices.items }; - } + }, + ( dispatch ) => { return { + removeNotice: ( noticeId ) => { + dispatch( removeNotice( noticeId ) ); + } + } } )( GlobalNotices ); diff --git a/client/state/notices/action-types.js b/client/state/notices/action-types.js index 199dbda1437eb..48b89db39dc1c 100644 --- a/client/state/notices/action-types.js +++ b/client/state/notices/action-types.js @@ -1 +1,2 @@ export const NEW_NOTICE = 'NEW_NOTICE'; +export const REMOVE_NOTICE = 'REMOVE_NOTICE'; diff --git a/client/state/notices/actions.js b/client/state/notices/actions.js index 4aa502c1f4b6b..e1d2a3f104786 100644 --- a/client/state/notices/actions.js +++ b/client/state/notices/actions.js @@ -2,11 +2,15 @@ * Internal dependencies */ import { - NEW_NOTICE + NEW_NOTICE, + REMOVE_NOTICE } from './action-types'; +import { uniqueId } from 'lodash'; + function newNotice( status, text, options ) { return { + noticeId: uniqueId(), type: NEW_NOTICE, status: status, text: text @@ -20,3 +24,10 @@ export function success( text, options ) { export function error( text, options ) { return newNotice( 'is-error', text, options ); } + +export function removeNotice( noticeId ) { + return { + noticeId: noticeId, + type: REMOVE_NOTICE + }; +} diff --git a/client/state/notices/reducers.js b/client/state/notices/reducers.js index da6c0885d7945..5208a60aa6126 100644 --- a/client/state/notices/reducers.js +++ b/client/state/notices/reducers.js @@ -2,12 +2,14 @@ * External dependencies */ import { combineReducers } from 'redux'; +import { filter } from 'lodash'; /** * Internal dependencies */ import { - NEW_NOTICE + NEW_NOTICE, + REMOVE_NOTICE } from './action-types'; /** @@ -22,6 +24,9 @@ export function items( state = [], action ) { case NEW_NOTICE: state = [ action, ...state ]; break; + case REMOVE_NOTICE: + state = filter( state, ( notice ) => ( notice.noticeId !== action.noticeId ) ); + break; } return state; From 5da94cbd869a320f420266c0d4e5b8169369abdc Mon Sep 17 00:00:00 2001 From: artpi Date: Fri, 11 Dec 2015 12:06:04 +0100 Subject: [PATCH 29/30] Notices: Add option duration --- client/me/notification-settings/index.jsx | 12 +++++++--- client/state/notices/actions.js | 28 ++++++++++++----------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/client/me/notification-settings/index.jsx b/client/me/notification-settings/index.jsx index eb9710d56d95e..33516f086503a 100644 --- a/client/me/notification-settings/index.jsx +++ b/client/me/notification-settings/index.jsx @@ -7,7 +7,7 @@ import React from 'react'; * Internal dependencies */ import observe from 'lib/mixins/data-observe'; -import { success, error } from 'state/notices/actions'; +import { success, error, removeNotice } from 'state/notices/actions'; import Main from 'components/main'; import ReauthRequired from 'me/reauth-required'; import twoStepAuthorization from 'lib/two-step-authorization'; @@ -48,7 +48,7 @@ const NotificationSettings = React.createClass( { } if ( state.status === 'success' ) { - this.props.successNotice( this.translate( 'Settings saved successfully!' ) ); + this.props.successNotice( this.translate( 'Settings saved successfully!' ), { duration: 3000 } ); } this.setState( state ); @@ -81,7 +81,13 @@ export default connect( }, ( dispatch ) => { return { successNotice: ( text, options ) => { - dispatch( success( text, options ) ); + var action = success( text, options ); + if ( action.duration > 0 ) { + setTimeout( () => { + dispatch( removeNotice( action.noticeId ) ); + }, action.duration ); + } + dispatch( action ); }, errorNotice: ( text, options ) => { dispatch( error( text, options ) ); diff --git a/client/state/notices/actions.js b/client/state/notices/actions.js index e1d2a3f104786..8915bab34828a 100644 --- a/client/state/notices/actions.js +++ b/client/state/notices/actions.js @@ -8,26 +8,28 @@ import { import { uniqueId } from 'lodash'; -function newNotice( status, text, options ) { - return { - noticeId: uniqueId(), - type: NEW_NOTICE, - status: status, - text: text - }; +function newNotice( status, text, options = {} ) { + return { + noticeId: uniqueId(), + duration: options.duration, + showDismiss: ( typeof options.showDismiss === 'boolean' ? options.showDismiss : true ), + type: NEW_NOTICE, + status: status, + text: text + }; } export function success( text, options ) { - return newNotice( 'is-success', text, options ); + return newNotice( 'is-success', text, options ); } export function error( text, options ) { - return newNotice( 'is-error', text, options ); + return newNotice( 'is-error', text, options ); } export function removeNotice( noticeId ) { - return { - noticeId: noticeId, - type: REMOVE_NOTICE - }; + return { + noticeId: noticeId, + type: REMOVE_NOTICE + }; } From a2f7f7954d4e09d0ccfe84e770566152318dd848 Mon Sep 17 00:00:00 2001 From: artpi Date: Fri, 11 Dec 2015 12:23:30 +0100 Subject: [PATCH 30/30] Notices: Move notices controller to actionCreators --- client/me/notification-settings/index.jsx | 17 ++------------ client/notices/global-notices.jsx | 8 ++----- client/state/notices/actionCreators.js | 27 +++++++++++++++++++++++ client/state/notices/actions.js | 12 ++-------- 4 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 client/state/notices/actionCreators.js diff --git a/client/me/notification-settings/index.jsx b/client/me/notification-settings/index.jsx index 33516f086503a..2cd7995586d40 100644 --- a/client/me/notification-settings/index.jsx +++ b/client/me/notification-settings/index.jsx @@ -7,7 +7,7 @@ import React from 'react'; * Internal dependencies */ import observe from 'lib/mixins/data-observe'; -import { success, error, removeNotice } from 'state/notices/actions'; +import noticeActionCreators from 'state/notices/actionCreators' import Main from 'components/main'; import ReauthRequired from 'me/reauth-required'; import twoStepAuthorization from 'lib/two-step-authorization'; @@ -79,18 +79,5 @@ export default connect( () => { return {} }, - ( dispatch ) => { return { - successNotice: ( text, options ) => { - var action = success( text, options ); - if ( action.duration > 0 ) { - setTimeout( () => { - dispatch( removeNotice( action.noticeId ) ); - }, action.duration ); - } - dispatch( action ); - }, - errorNotice: ( text, options ) => { - dispatch( error( text, options ) ); - } - } } + noticeActionCreators )( NotificationSettings ); diff --git a/client/notices/global-notices.jsx b/client/notices/global-notices.jsx index c1506eb7b3f11..e71f87cb6903d 100644 --- a/client/notices/global-notices.jsx +++ b/client/notices/global-notices.jsx @@ -5,7 +5,7 @@ import React from 'react'; import classNames from 'classnames'; import debugModule from 'debug'; import { connect } from 'react-redux'; -import { removeNotice } from 'state/notices/actions' +import noticeActionCreators from 'state/notices/actionCreators' /** * Internal Dependencies @@ -83,9 +83,5 @@ export default connect( notices: state.notices.items }; }, - ( dispatch ) => { return { - removeNotice: ( noticeId ) => { - dispatch( removeNotice( noticeId ) ); - } - } } + noticeActionCreators )( GlobalNotices ); diff --git a/client/state/notices/actionCreators.js b/client/state/notices/actionCreators.js new file mode 100644 index 0000000000000..d3e3056d0b84e --- /dev/null +++ b/client/state/notices/actionCreators.js @@ -0,0 +1,27 @@ +import { createNoticeAction, removeNoticeAction } from './actions'; + +export default function( dispatch ) { + function createNotice( type, text, options ) { + var action = createNoticeAction( type, text, options ); + + if ( action.duration > 0 ) { + setTimeout( () => { + dispatch( removeNoticeAction( action.noticeId ) ); + }, action.duration ); + } + + dispatch( action ); + } + + return { + successNotice: ( text, options ) => { + createNotice( 'is-success', text, options ); + }, + errorNotice: ( text, options ) => { + createNotice( 'is-error', text, options ); + }, + removeNotice: ( noticeId ) => { + dispatch( removeNoticeAction( noticeId ) ); + } + }; +} diff --git a/client/state/notices/actions.js b/client/state/notices/actions.js index 8915bab34828a..218969c807f18 100644 --- a/client/state/notices/actions.js +++ b/client/state/notices/actions.js @@ -8,7 +8,7 @@ import { import { uniqueId } from 'lodash'; -function newNotice( status, text, options = {} ) { +export function createNoticeAction( status, text, options = {} ) { return { noticeId: uniqueId(), duration: options.duration, @@ -19,15 +19,7 @@ function newNotice( status, text, options = {} ) { }; } -export function success( text, options ) { - return newNotice( 'is-success', text, options ); -} - -export function error( text, options ) { - return newNotice( 'is-error', text, options ); -} - -export function removeNotice( noticeId ) { +export function removeNoticeAction( noticeId ) { return { noticeId: noticeId, type: REMOVE_NOTICE