From 324c267f8a2fc01997cfc94bd00e98c1174391e5 Mon Sep 17 00:00:00 2001 From: William Petit Date: Thu, 3 Sep 2015 15:50:23 +0200 Subject: [PATCH] Mise en place base Redux --- css/style.css | 2 +- js/actions/edit.js | 29 ++++++++++++ js/actions/index.js | 2 + js/actions/launcher.js | 23 ++++++++++ js/app.jsx | 11 +++-- js/components/app-icon.jsx | 71 ++++++++++++++++++++++++++++++ js/components/desktop-app-item.jsx | 59 ++----------------------- js/components/desktop-app-list.jsx | 21 ++------- js/components/edit-view.jsx | 18 +++++++- js/stores/index.js | 21 +++++++++ js/stores/reducers/desktop-apps.js | 13 ++++++ js/stores/reducers/index.js | 3 ++ js/stores/reducers/process-opts.js | 8 ++++ js/stores/reducers/profile.js | 3 ++ package.json | 7 ++- 15 files changed, 210 insertions(+), 81 deletions(-) create mode 100644 js/actions/edit.js create mode 100644 js/actions/index.js create mode 100644 js/actions/launcher.js create mode 100644 js/components/app-icon.jsx create mode 100644 js/stores/index.js create mode 100644 js/stores/reducers/desktop-apps.js create mode 100644 js/stores/reducers/index.js create mode 100644 js/stores/reducers/process-opts.js create mode 100644 js/stores/reducers/profile.js diff --git a/css/style.css b/css/style.css index 3de453b..3ad4842 100644 --- a/css/style.css +++ b/css/style.css @@ -122,7 +122,7 @@ html, body { } -.edit img.desktop-app-icon { +.edit .desktop-app > .app-icon { height: 50px; width: 50px; display: inline-block; diff --git a/js/actions/edit.js b/js/actions/edit.js new file mode 100644 index 0000000..c94e344 --- /dev/null +++ b/js/actions/edit.js @@ -0,0 +1,29 @@ +var Util = require('../util'); +var path = require('path'); + +// Action types +var LOAD_DESKTOP_APPS = exports.LOAD_PROFILE = 'LOAD_DESKTOP_APPS'; +var LOAD_DESKTOP_APPS_SUCCESS = exports.LOAD_DESKTOP_APPS_SUCCESS = 'LOAD_DESKTOP_APPS_SUCCESS'; +var LOAD_DESKTOP_APPS_FAILED = exports.LOAD_DESKTOP_APPS_FAILED = 'LOAD_DESKTOP_APPS_FAILED'; + +// Actions creator +exports.loadDesktopApps = function() { + return function(dispatch, getState) { + + var baseDirs = global.process.env.XDG_DATA_DIRS.split(':').map(function(baseDir){ + return path.join(baseDir, 'applications'); + }); + + dispatch({ type: LOAD_DESKTOP_APPS }); + + return Util.DesktopApps.loadAllDesktopFiles(baseDirs) + .then(function(desktopApps) { + dispatch({ type: LOAD_DESKTOP_APPS_SUCCESS, desktopApps: desktopApps }); + }) + .catch(function(err) { + dispatch({ type: LOAD_DESKTOP_APPS_FAILED, error: err }); + }) + ; + + }; +}; diff --git a/js/actions/index.js b/js/actions/index.js new file mode 100644 index 0000000..cbeddd1 --- /dev/null +++ b/js/actions/index.js @@ -0,0 +1,2 @@ +exports.mauncher = require('./launcher'); +exports.edit = require('./edit'); diff --git a/js/actions/launcher.js b/js/actions/launcher.js new file mode 100644 index 0000000..6203d17 --- /dev/null +++ b/js/actions/launcher.js @@ -0,0 +1,23 @@ +var Util = require('../util'); + +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'; + +exports.loadProfile = function(profilePath) { + + return function(dispatch, getState) { + + dispatch({ type: LOAD_PROFILE }); + + return Util.System.loadJSONFile(profilePath) + .then(function(profile) { + dispatch({ type: LOAD_PROFILE_SUCCESS, profile: profile }); + }) + .catch(function(err) { + dispatch({ type: LOAD_PROFILE_FAILED, error: err }); + }) + ; + + }; +}; diff --git a/js/app.jsx b/js/app.jsx index 43715eb..ae43c89 100644 --- a/js/app.jsx +++ b/js/app.jsx @@ -3,12 +3,13 @@ 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 Provider = require('react-redux').Provider; +var stores = require('./stores'); // Internal constants var DEFAULT_PROFILE = './default-profile.json'; var PROCESS_OPTS = minimist(gui.App.argv); - // Main component var App = React.createClass({ @@ -22,8 +23,12 @@ var App = React.createClass({ render: function() { var view = this.state.editMode ? - : - + + { function() { return ; }.bind(this) } + : + + { function() { return ; }.bind(this) } + ; return ( diff --git a/js/components/app-icon.jsx b/js/components/app-icon.jsx new file mode 100644 index 0000000..0490f78 --- /dev/null +++ b/js/components/app-icon.jsx @@ -0,0 +1,71 @@ +var React = require('react'); +var Util = require('../util'); +var LazyLoad = require('./mixins/lazy-load'); + +var LOADING_ICON = 'img/hourglass.svg'; +var DEFAULT_ICON = 'img/default-icon.svg'; + +module.exports = React.createClass({ + + mixins: [LazyLoad], + + getInitialState: function() { + return { icon: DEFAULT_ICON, currentTheme: undefined }; + }, + + onInViewport: function() { + this.updateIconIfInViewport(); + }, + + updateIconIfInViewport: function() { + + var currentTheme = this.state.currentTheme; + var newTheme = this.props.theme; + + if( !this.isInViewport() || newTheme === currentTheme ) return; + + this.setState({ icon: LOADING_ICON, currentTheme: newTheme }); + + var desktopEntry = this.props.desktopEntry; + + this._findIcon(this.props.icon, newTheme); + + }, + + componentDidUpdate: function() { + this.updateIconIfInViewport(); + }, + + render: function() { + + var icon = this.state.icon; + + return ( + + ); + + }, + + _findIcon: function(iconPath, theme) { + + var self = this; + + console.log('Search icon %s:%s', iconPath, theme); + + Util.DesktopApps.findIcon(iconPath || DEFAULT_ICON, theme) + .then(function(iconPath) { + if( !iconPath || /\.xpm$/.test(iconPath) ) { + return Util.DesktopApps.findIcon(DEFAULT_ICON, theme); + } + return iconPath; + }) + .then(function(iconPath) { + global.window.requestAnimationFrame(function() { + self.setState({ icon: iconPath }); + }); + }) + ; + + } + +}); diff --git a/js/components/desktop-app-item.jsx b/js/components/desktop-app-item.jsx index f58cda6..d8499ef 100644 --- a/js/components/desktop-app-item.jsx +++ b/js/components/desktop-app-item.jsx @@ -1,76 +1,23 @@ var React = require('react'); var Util = require('../util'); -var LazyLoad = require('./mixins/lazy-load'); - -var LOADING_ICON = 'img/hourglass.svg'; -var DEFAULT_ICON = 'img/default-icon.svg'; +var AppIcon = require('./app-icon.jsx'); module.exports = React.createClass({ - mixins: [LazyLoad], - - getInitialState: function() { - return { icon: LOADING_ICON, currentTheme: undefined }; - }, - - onInViewport: function() { - this.updateIconIfInViewport(); - }, - - updateIconIfInViewport: function() { - - var currentTheme = this.state.currentTheme; - var newTheme = this.props.theme; - - if( !this.isInViewport() || newTheme === currentTheme ) return; - - this.setState({ icon: LOADING_ICON, currentTheme: newTheme }); - - var desktopEntry = this.props.desktopEntry; - - this._findIcon(desktopEntry.Icon, newTheme); - - }, - - componentDidUpdate: function() { - this.updateIconIfInViewport(); - }, - render: function() { var desktopEntry = this.props.desktopEntry; var label = desktopEntry.Name; var category = desktopEntry.Categories; + var icon = desktopEntry.Icon; return (
  • - + {label}
  • ); - }, - - _findIcon: function(iconPath, theme) { - - var self = this; - - console.log('Search icon %s:%s', iconPath, theme); - - Util.DesktopApps.findIcon(iconPath || DEFAULT_ICON, theme) - .then(function(iconPath) { - if( !iconPath || /\.xpm$/.test(iconPath) ) { - return Util.DesktopApps.findIcon(DEFAULT_ICON, theme); - } - return iconPath; - }) - .then(function(iconPath) { - global.window.requestAnimationFrame(function() { - self.setState({ icon: iconPath }); - }); - }) - ; - } }); diff --git a/js/components/desktop-app-list.jsx b/js/components/desktop-app-list.jsx index 07b5dca..28a8368 100644 --- a/js/components/desktop-app-list.jsx +++ b/js/components/desktop-app-list.jsx @@ -9,30 +9,15 @@ module.exports = React.createClass({ getInitialState: function() { return { - desktopFiles: [], selectedTheme: null }; }, - componentDidMount: function() { - - // Load system desktop apps - var baseDirs = global.process.env.XDG_DATA_DIRS.split(':').map(function(baseDir){ - return path.join(baseDir, 'applications'); - }); - - Util.DesktopApps.loadAllDesktopFiles('/usr/share/applications') - .then(function(desktopFiles) { - this.setState({ desktopFiles: desktopFiles }); - }.bind(this)) - ; - }, - render: function() { - var items = this.state.desktopFiles.map(function(desktopFile, i) { - var desktopEntry = desktopFile.content['Desktop Entry']; - return ; + var items = this.props.desktopApps.map(function(desktopApp, i) { + var desktopEntry = desktopApp.content['Desktop Entry']; + return ; }.bind(this)); return ( diff --git a/js/components/edit-view.jsx b/js/components/edit-view.jsx index 96922ca..8e874f0 100644 --- a/js/components/edit-view.jsx +++ b/js/components/edit-view.jsx @@ -1,16 +1,30 @@ var React = require('react'); +var connect = require('react-redux').connect; var DesktopAppList = require('./desktop-app-list.jsx'); +var actions = require('../actions'); -module.exports = React.createClass({ +var EditView = React.createClass({ + + componentDidMount: function() { + this.props.dispatch(actions.edit.loadDesktopApps()); + }, render: function() { return (
    - +
    ); } }); + +function select(state) { + return { + desktopApps: state.desktopApps + }; +} + +module.exports = connect(select)(EditView); diff --git a/js/stores/index.js b/js/stores/index.js new file mode 100644 index 0000000..b50fb43 --- /dev/null +++ b/js/stores/index.js @@ -0,0 +1,21 @@ +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/desktop-apps.js b/js/stores/reducers/desktop-apps.js new file mode 100644 index 0000000..12f0976 --- /dev/null +++ b/js/stores/reducers/desktop-apps.js @@ -0,0 +1,13 @@ +var actions = require('../../actions'); + +module.exports = function(state, action) { + + var desktopApps = []; + + if( action.type === actions.edit.LOAD_DESKTOP_APPS_SUCCESS ) { + desktopApps = action.desktopApps; + } + + return desktopApps; + +}; diff --git a/js/stores/reducers/index.js b/js/stores/reducers/index.js new file mode 100644 index 0000000..4927732 --- /dev/null +++ b/js/stores/reducers/index.js @@ -0,0 +1,3 @@ +exports.desktopApps = require('./desktop-apps'); +exports.profile = require('./profile'); +exports.processOpts = require('./process-opts'); diff --git a/js/stores/reducers/process-opts.js b/js/stores/reducers/process-opts.js new file mode 100644 index 0000000..f9b8964 --- /dev/null +++ b/js/stores/reducers/process-opts.js @@ -0,0 +1,8 @@ +var minimist = require('minimist'); +var gui = global.window.require('nw.gui'); + +var opts = minimist(gui.App.argv); + +module.exports = function(state, action) { + return opts; +}; diff --git a/js/stores/reducers/profile.js b/js/stores/reducers/profile.js new file mode 100644 index 0000000..b03e048 --- /dev/null +++ b/js/stores/reducers/profile.js @@ -0,0 +1,3 @@ +module.exports = function(state, action) { + return {}; +}; diff --git a/package.json b/package.json index 2b019cf..29ef229 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,17 @@ "kiosk": false }, "dependencies": { + "bootstrap": "^3.3.5", "debug": "^2.2.0", "glob": "^5.0.14", "ini": "^1.3.4", "minimist": "^1.1.3", "node-jsx": "^0.13.3", "react": "^0.13.3", - "react-dnd": "^1.1.5" + "react-dnd": "^1.1.5", + "react-redux": "^2.0.0", + "redux": "^2.0.0", + "redux-logger": "^1.0.6", + "redux-thunk": "^0.1.0" } }