From 06c809a114376bea2f1c41aa5975673ceaf0acb7 Mon Sep 17 00:00:00 2001 From: William Petit Date: Fri, 4 Sep 2015 12:10:08 +0200 Subject: [PATCH] Refactoring + mise en place store Redux --- default-profile.json | 12 ++-- js/actions/index.js | 2 +- js/actions/launcher.js | 30 +++++++++- js/app.jsx | 50 +++++++++------- js/components/{ => common}/app-icon.jsx | 11 ++-- js/components/{ => edit}/desktop-app-item.jsx | 4 +- js/components/{ => edit}/desktop-app-list.jsx | 2 +- js/components/{ => edit}/edit-view.jsx | 2 +- .../{ => edit}/icon-theme-selector.jsx | 2 +- js/components/{ => launcher}/app-item.jsx | 3 +- js/components/{ => launcher}/app-list.jsx | 0 .../{ => launcher}/category-header.jsx | 0 .../{ => launcher}/launcher-view.jsx | 57 +++++++++++-------- js/store/index.js | 16 ++++++ js/{stores => store}/reducers/desktop-apps.js | 0 js/{stores => store}/reducers/index.js | 0 js/{stores => store}/reducers/process-opts.js | 0 js/store/reducers/profile.js | 17 ++++++ js/stores/index.js | 21 ------- js/stores/reducers/profile.js | 3 - js/util/debug.js | 12 ++++ js/util/desktop-apps.js | 20 ++++++- js/util/index.js | 1 + package.json | 1 - 24 files changed, 175 insertions(+), 91 deletions(-) rename js/components/{ => common}/app-icon.jsx (84%) rename js/components/{ => edit}/desktop-app-item.jsx (85%) rename js/components/{ => edit}/desktop-app-list.jsx (96%) rename js/components/{ => edit}/edit-view.jsx (93%) rename js/components/{ => edit}/icon-theme-selector.jsx (96%) rename js/components/{ => launcher}/app-item.jsx (85%) rename js/components/{ => launcher}/app-list.jsx (100%) rename js/components/{ => launcher}/category-header.jsx (100%) rename js/components/{ => launcher}/launcher-view.jsx (67%) create mode 100644 js/store/index.js rename js/{stores => store}/reducers/desktop-apps.js (100%) rename js/{stores => store}/reducers/index.js (100%) rename js/{stores => store}/reducers/process-opts.js (100%) create mode 100644 js/store/reducers/profile.js delete mode 100644 js/stores/index.js delete mode 100644 js/stores/reducers/profile.js create mode 100644 js/util/debug.js diff --git a/default-profile.json b/default-profile.json index aa9154f..864289a 100644 --- a/default-profile.json +++ b/default-profile.json @@ -2,30 +2,30 @@ "items": [ { "label": "Level 1", - "icon": "/usr/share/icons/Mint-X/apps/48/chromium-browser.png", + "icon": "chromium-browser", "items": [ { "label": "Level 2-1", - "icon": "/usr/share/icons/Mint-X/apps/48/chromium-browser.png", + "icon": "chromium-browser", "items": [ { "label": "Chromium Browser", - "icon": "/usr/share/icons/Mint-X/apps/48/chromium-browser.png", + "icon": "chromium-browser", "exec": "/usr/bin/chromium-browser" } ] }, { "label": "Level 2-2", - "icon": "/usr/share/icons/Mint-X/apps/48/chromium-browser.png", + "icon": "chromium-browser", "items": [ { "label": "Level 3-1", - "icon": "/usr/share/icons/Mint-X/apps/48/chromium-browser.png", + "icon": "chromium-browser", "items": [ { "label": "Chromium Browser", - "icon": "/usr/share/icons/Mint-X/apps/48/chromium-browser.png", + "icon": "chromium-browser", "exec": "/usr/bin/chromium-browser" } ] diff --git a/js/actions/index.js b/js/actions/index.js index cbeddd1..4b05d3a 100644 --- a/js/actions/index.js +++ b/js/actions/index.js @@ -1,2 +1,2 @@ -exports.mauncher = require('./launcher'); +exports.launcher = require('./launcher'); exports.edit = require('./edit'); diff --git a/js/actions/launcher.js b/js/actions/launcher.js index 6203d17..da2f9bc 100644 --- a/js/actions/launcher.js +++ b/js/actions/launcher.js @@ -4,20 +4,48 @@ var LOAD_PROFILE = exports.LOAD_PROFILE = 'LOAD_PROFILE'; var LOAD_PROFILE_SUCCESS = exports.LOAD_PROFILE_SUCCESS = 'LOAD_PROFILE_SUCCESS'; var LOAD_PROFILE_FAILED = exports.LOAD_PROFILE_FAILED = 'LOAD_PROFILE_FAILED'; +var RUN_APP = exports.RUN_APP = 'RUN_APP'; +var RUN_APP_SUCCESS = exports.RUN_APP_SUCCESS = 'RUN_APP_SUCCESS'; +var RUN_APP_FAILED = exports.RUN_APP_FAILED = 'RUN_APP_FAILED'; + exports.loadProfile = function(profilePath) { return function(dispatch, getState) { - dispatch({ type: LOAD_PROFILE }); + dispatch({ type: LOAD_PROFILE, profilePath: profilePath }); return Util.System.loadJSONFile(profilePath) .then(function(profile) { dispatch({ type: LOAD_PROFILE_SUCCESS, profile: profile }); + return profile; }) .catch(function(err) { dispatch({ type: LOAD_PROFILE_FAILED, error: err }); + return err; }) ; }; + +}; + +exports.runApp = function(execPath) { + + return function(dispatch, getState) { + + dispatch({ type: RUN_APP, execPath: execPath }); + + return Util.System.runApp(execPath) + .then(function() { + dispatch({ type: RUN_APP_SUCCESS, execPath: execPath }); + return execPath; + }) + .catch(function(err) { + dispatch({ type: RUN_APP_FAILED, error: err }); + return err; + }) + ; + + }; + }; diff --git a/js/app.jsx b/js/app.jsx index ae43c89..c52e8b2 100644 --- a/js/app.jsx +++ b/js/app.jsx @@ -1,33 +1,23 @@ var React = require('react'); -var minimist = require('minimist'); -var gui = global.window.require('nw.gui'); -var LauncherView = require('./components/launcher-view.jsx'); -var EditView = require('./components/edit-view.jsx'); +var LauncherView = require('./components/launcher/launcher-view.jsx'); +var EditView = require('./components/edit/edit-view.jsx'); var Provider = require('react-redux').Provider; -var stores = require('./stores'); - -// Internal constants -var DEFAULT_PROFILE = './default-profile.json'; -var PROCESS_OPTS = minimist(gui.App.argv); +var connect = require('react-redux').connect; +var store = require('./store'); // Main component var App = React.createClass({ - getInitialState: function() { - return { - profilePath: PROCESS_OPTS.profile, - editMode: PROCESS_OPTS.edit || false - }; - }, - render: function() { - var view = this.state.editMode ? - - { function() { return ; }.bind(this) } + var editMode = this.props.processOpts.edit || false; + + var view = editMode ? + + { function() { return ; }.bind(this) } : - - { function() { return ; }.bind(this) } + + { function() { return ; }.bind(this) } ; @@ -41,4 +31,20 @@ var App = React.createClass({ }); -React.render(, document.body); +// Select props to inject from store state +function select(state) { + return { + processOpts: state.processOpts + } +} + +// Connect App to Redux store +App = connect(select)(App); + +React.render( + + { function() { return ; } } + + , + document.body +); diff --git a/js/components/app-icon.jsx b/js/components/common/app-icon.jsx similarity index 84% rename from js/components/app-icon.jsx rename to js/components/common/app-icon.jsx index 0490f78..c9f857a 100644 --- a/js/components/app-icon.jsx +++ b/js/components/common/app-icon.jsx @@ -1,6 +1,7 @@ var React = require('react'); -var Util = require('../util'); -var LazyLoad = require('./mixins/lazy-load'); +var Util = require('../../util'); +var LazyLoad = require('../mixins/lazy-load'); +var debug = Util.Debug('common:app-icon'); var LOADING_ICON = 'img/hourglass.svg'; var DEFAULT_ICON = 'img/default-icon.svg'; @@ -50,7 +51,7 @@ module.exports = React.createClass({ var self = this; - console.log('Search icon %s:%s', iconPath, theme); + debug('Search icon %s:%s', iconPath, theme); Util.DesktopApps.findIcon(iconPath || DEFAULT_ICON, theme) .then(function(iconPath) { @@ -60,9 +61,7 @@ module.exports = React.createClass({ return iconPath; }) .then(function(iconPath) { - global.window.requestAnimationFrame(function() { - self.setState({ icon: iconPath }); - }); + self.setState({ icon: iconPath }); }) ; diff --git a/js/components/desktop-app-item.jsx b/js/components/edit/desktop-app-item.jsx similarity index 85% rename from js/components/desktop-app-item.jsx rename to js/components/edit/desktop-app-item.jsx index d8499ef..b528aa1 100644 --- a/js/components/desktop-app-item.jsx +++ b/js/components/edit/desktop-app-item.jsx @@ -1,6 +1,6 @@ var React = require('react'); -var Util = require('../util'); -var AppIcon = require('./app-icon.jsx'); +var Util = require('../../util'); +var AppIcon = require('../common/app-icon.jsx'); module.exports = React.createClass({ diff --git a/js/components/desktop-app-list.jsx b/js/components/edit/desktop-app-list.jsx similarity index 96% rename from js/components/desktop-app-list.jsx rename to js/components/edit/desktop-app-list.jsx index 28a8368..b426027 100644 --- a/js/components/desktop-app-list.jsx +++ b/js/components/edit/desktop-app-list.jsx @@ -1,5 +1,5 @@ var React = require('react'); -var Util = require('../util'); +var Util = require('../../util'); var DesktopAppItem = require('./desktop-app-item.jsx'); var IconThemeSelector = require('./icon-theme-selector.jsx'); var path = require('path'); diff --git a/js/components/edit-view.jsx b/js/components/edit/edit-view.jsx similarity index 93% rename from js/components/edit-view.jsx rename to js/components/edit/edit-view.jsx index 8e874f0..419f01e 100644 --- a/js/components/edit-view.jsx +++ b/js/components/edit/edit-view.jsx @@ -1,7 +1,7 @@ var React = require('react'); var connect = require('react-redux').connect; var DesktopAppList = require('./desktop-app-list.jsx'); -var actions = require('../actions'); +var actions = require('../../actions'); var EditView = React.createClass({ diff --git a/js/components/icon-theme-selector.jsx b/js/components/edit/icon-theme-selector.jsx similarity index 96% rename from js/components/icon-theme-selector.jsx rename to js/components/edit/icon-theme-selector.jsx index aa2c239..73fbac1 100644 --- a/js/components/icon-theme-selector.jsx +++ b/js/components/edit/icon-theme-selector.jsx @@ -1,5 +1,5 @@ var React = require('react'); -var Util = require('../util'); +var Util = require('../../util'); module.exports = React.createClass({ diff --git a/js/components/app-item.jsx b/js/components/launcher/app-item.jsx similarity index 85% rename from js/components/app-item.jsx rename to js/components/launcher/app-item.jsx index 37c58ac..8ffb14a 100644 --- a/js/components/app-item.jsx +++ b/js/components/launcher/app-item.jsx @@ -1,4 +1,5 @@ var React = require('react'); +var AppIcon = require('../common/app-icon.jsx'); module.exports = React.createClass({ @@ -19,7 +20,7 @@ module.exports = React.createClass({ render: function() { return (
  • - + {this.props.item.label}
  • ); diff --git a/js/components/app-list.jsx b/js/components/launcher/app-list.jsx similarity index 100% rename from js/components/app-list.jsx rename to js/components/launcher/app-list.jsx diff --git a/js/components/category-header.jsx b/js/components/launcher/category-header.jsx similarity index 100% rename from js/components/category-header.jsx rename to js/components/launcher/category-header.jsx diff --git a/js/components/launcher-view.jsx b/js/components/launcher/launcher-view.jsx similarity index 67% rename from js/components/launcher-view.jsx rename to js/components/launcher/launcher-view.jsx index 358e03b..c82050d 100644 --- a/js/components/launcher-view.jsx +++ b/js/components/launcher/launcher-view.jsx @@ -1,17 +1,17 @@ var React = require('react'); -var Util = require('../util'); var CategoryHeader = require('./category-header.jsx'); var AppList = require('./app-list.jsx'); -var AnimateMixin = require('./mixins/animate'); +var AnimateMixin = require('../mixins/animate'); +var actions = require('../../actions'); +var connect = require('react-redux').connect; +var debug = require('../../util/debug')('launcher-view'); -module.exports = React.createClass({ +var DEFAULT_PROFILE = './default-profile.json'; + +var LauncherView = React.createClass({ mixins: [AnimateMixin], - propTypes: { - profilePath: React.PropTypes.string.isRequired - }, - getInitialState: function() { return { currentItemPath: '', @@ -20,20 +20,20 @@ module.exports = React.createClass({ }, componentDidMount: function() { + var profilePath = this.props.processOpts.profile || DEFAULT_PROFILE; + this.props.dispatch(actions.launcher.loadProfile(profilePath)); + }, - // Load profile on component mount - Util.System.loadJSONFile(this.props.profilePath) - .then(function(profile) { - this.setState({ profile: profile, currentItem: profile, currentItemPath: '' }); - }.bind(this)) - ; - + componentWillReceiveProps: function(nextProps) { + if( nextProps.profile && !this.state.currentItem ) { + this.setState({ currentItem: nextProps.profile }); + } }, render: function() { var currentItem = this.state.currentItem; - var items = currentItem ? currentItem.items : []; + var items = currentItem && currentItem.items ? currentItem.items : []; var currentItemPath = this.state.currentItemPath; var header = currentItemPath !== '' ? @@ -59,7 +59,7 @@ module.exports = React.createClass({ onBackClick: function(itemPath) { var parentPath = this._normalizeItemPath(itemPath).slice(0, -1); - var parentItem = this._getItemByPath(parentPath); + var parentItem = this._getItemByPath(parentPath, this.props.profile); this.play(this.refs.appList, 'slide-out-right 250ms ease-in-out') .then(function() { @@ -74,15 +74,16 @@ module.exports = React.createClass({ if(item.exec) { - console.info('Launching application "'+item.exec+'"...'); - evt.currentTarget.classList.add('pulse'); + debug('Launching application "'+item.exec+'"...'); + var el = evt.currentTarget; + el.classList.add('pulse'); - Util.System.runApp(item.exec) + this.props.dispatch(actions.launcher.runApp(item.exec)) .then(function() { - evt.currentTarget.classList.remove('pulse'); + el.classList.remove('pulse'); }) - .catch(function(err) { - evt.currentTarget.classList.remove('pulse'); + .catch(function() { + el.classList.remove('pulse'); }) ; @@ -99,7 +100,6 @@ module.exports = React.createClass({ _getItemByPath: function(itemPath, rootItem) { - rootItem = rootItem || this.state.profile; itemPath = this._normalizeItemPath(itemPath); var itemIndex = itemPath[0]; @@ -138,3 +138,14 @@ module.exports = React.createClass({ } }); + + +function select(state) { + return { + processOpts: state.processOpts, + profile: state.profile + }; +} + + +module.exports = connect(select)(LauncherView); diff --git a/js/store/index.js b/js/store/index.js new file mode 100644 index 0000000..165da34 --- /dev/null +++ b/js/store/index.js @@ -0,0 +1,16 @@ +var redux = require('redux'); +var thunkMiddleware = require('redux-thunk'); +var loggerMiddleware = require('redux-logger'); +var reducers = require('./reducers'); + +var createStore = redux.applyMiddleware( + thunkMiddleware +)(redux.createStore); + +var appReducer = redux.combineReducers({ + profile: reducers.profile, + processOpts: reducers.processOpts, + desktopApps: reducers.desktopApps +}); + +module.exports = createStore(appReducer); diff --git a/js/stores/reducers/desktop-apps.js b/js/store/reducers/desktop-apps.js similarity index 100% rename from js/stores/reducers/desktop-apps.js rename to js/store/reducers/desktop-apps.js diff --git a/js/stores/reducers/index.js b/js/store/reducers/index.js similarity index 100% rename from js/stores/reducers/index.js rename to js/store/reducers/index.js diff --git a/js/stores/reducers/process-opts.js b/js/store/reducers/process-opts.js similarity index 100% rename from js/stores/reducers/process-opts.js rename to js/store/reducers/process-opts.js diff --git a/js/store/reducers/profile.js b/js/store/reducers/profile.js new file mode 100644 index 0000000..7db6b33 --- /dev/null +++ b/js/store/reducers/profile.js @@ -0,0 +1,17 @@ +var actions = require('../../actions'); + +module.exports = function(oldProfile, action) { + + var newProfile = oldProfile || null; + + switch(action.type) { + + case actions.launcher.LOAD_PROFILE_SUCCESS: + newProfile = action.profile; + break; + + } + + return newProfile; + +}; diff --git a/js/stores/index.js b/js/stores/index.js deleted file mode 100644 index b50fb43..0000000 --- a/js/stores/index.js +++ /dev/null @@ -1,21 +0,0 @@ -var redux = require('redux'); -var thunkMiddleware = require('redux-thunk'); -var loggerMiddleware = require('redux-logger'); -var reducers = require('./reducers'); - -var createStoreWithMiddleware = redux.applyMiddleware( - thunkMiddleware -)(redux.createStore); - -var launcherReducers = redux.combineReducers({ - profile: reducers.profile, - processOpts: reducers.processOpts -}); - -var editReducers = redux.combineReducers({ - desktopApps: reducers.desktopApps, - processOpts: reducers.processOpts -}); - -exports.launcherStore = createStoreWithMiddleware(launcherReducers); -exports.editStore = createStoreWithMiddleware(editReducers); diff --git a/js/stores/reducers/profile.js b/js/stores/reducers/profile.js deleted file mode 100644 index b03e048..0000000 --- a/js/stores/reducers/profile.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function(state, action) { - return {}; -}; diff --git a/js/util/debug.js b/js/util/debug.js new file mode 100644 index 0000000..97ecd7f --- /dev/null +++ b/js/util/debug.js @@ -0,0 +1,12 @@ +var debug = require('debug'); +var util = require('util'); + +module.exports = function createLogger(namespace) { + var logger = debug('pitaya:'+namespace); + var console = global.window ? global.window.console : global.console; + logger.log = function() { + var str = util.format.apply(util, arguments); + console.log(str); + }; + return logger; +}; diff --git a/js/util/desktop-apps.js b/js/util/desktop-apps.js index 1821a3a..385fbe2 100644 --- a/js/util/desktop-apps.js +++ b/js/util/desktop-apps.js @@ -1,6 +1,7 @@ var path = require('path'); var System = require('./system'); -var debug = require('debug')('pitaya:desktop-apps'); +var debug = require('./debug')('desktop-apps'); +var Cache = require('./cache'); // Constants var ICON_REALPATH_REGEX = /\..+$/; @@ -69,6 +70,8 @@ exports.loadDesktopFile = function(filePath) { return System.loadINIFile(filePath); }; +var iconCache = new Cache(); + /** * Find the absolute path of a desktop icon * @@ -77,6 +80,13 @@ exports.loadDesktopFile = function(filePath) { */ exports.findIcon = function(iconName, themeName, size, themeIgnore) { + var cachedIcon = iconCache.get([iconName, themeName, size]); + + if(cachedIcon) { + debug('Icon %s:%s:%s found in cache !', iconName, themeName, size); + return Promise.resolve(cachedIcon); + } + themeIgnore = themeIgnore || []; if(themeIgnore.indexOf(themeIgnore) !== -1) { debug('Theme %s already processed, ignoring...', themeName); @@ -101,6 +111,7 @@ exports.findIcon = function(iconName, themeName, size, themeIgnore) { .then(exports._selectBestIcon) ; }) + .then(_cacheIcon) ; } @@ -122,8 +133,15 @@ exports.findIcon = function(iconName, themeName, size, themeIgnore) { ; }) + .then(_cacheIcon) ; + + function _cacheIcon(iconPath) { + iconCache.set([iconName, themeName, size], iconPath); + return iconPath; + } + }; exports.findParentsThemeIcon = function(iconName, themeName, size, themeIgnore) { diff --git a/js/util/index.js b/js/util/index.js index 220f14b..49a2fea 100644 --- a/js/util/index.js +++ b/js/util/index.js @@ -1,3 +1,4 @@ exports.System = require('./system'); exports.DesktopApps = require('./desktop-apps'); exports.Cache = require('./cache'); +exports.Debug = require('./debug'); diff --git a/package.json b/package.json index 29ef229..e5a1d88 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "react-dnd": "^1.1.5", "react-redux": "^2.0.0", "redux": "^2.0.0", - "redux-logger": "^1.0.6", "redux-thunk": "^0.1.0" } }