Mise en place base Redux

This commit is contained in:
wpetit 2015-09-03 15:50:23 +02:00
parent 411894586b
commit 324c267f8a
15 changed files with 210 additions and 81 deletions

View File

@ -122,7 +122,7 @@ html, body {
}
.edit img.desktop-app-icon {
.edit .desktop-app > .app-icon {
height: 50px;
width: 50px;
display: inline-block;

29
js/actions/edit.js Normal file
View File

@ -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 });
})
;
};
};

2
js/actions/index.js Normal file
View File

@ -0,0 +1,2 @@
exports.mauncher = require('./launcher');
exports.edit = require('./edit');

23
js/actions/launcher.js Normal file
View File

@ -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 });
})
;
};
};

View File

@ -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 ?
<EditView profilePath={this.state.profilePath} /> :
<LauncherView profilePath={this.state.profilePath ? this.state.profilePath : DEFAULT_PROFILE } />
<Provider store={stores.editStore}>
{ function() { return <EditView profilePath={this.state.profilePath} />; }.bind(this) }
</Provider> :
<Provider store={stores.launcherStore}>
{ function() { return <LauncherView profilePath={this.state.profilePath ? this.state.profilePath : DEFAULT_PROFILE } />; }.bind(this) }
</Provider>
;
return (

View File

@ -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 (
<img src={icon} className="app-icon" />
);
},
_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 });
});
})
;
}
});

View File

@ -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 (
<li className="desktop-app">
<img src={this.state.icon} className="desktop-app-icon" />
<AppIcon className="desktop-app-icon" icon={icon} theme={this.props.theme} />
<span className="desktop-app-label">{label}</span>
</li>
);
},
_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 });
});
})
;
}
});

View File

@ -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 <DesktopAppItem theme={this.state.selectedTheme} key={desktopFile.path} desktopEntry={desktopEntry} />;
var items = this.props.desktopApps.map(function(desktopApp, i) {
var desktopEntry = desktopApp.content['Desktop Entry'];
return <DesktopAppItem theme={this.state.selectedTheme} key={desktopApp.path} desktopEntry={desktopEntry} />;
}.bind(this));
return (

View File

@ -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 (
<div className="edit">
<DesktopAppList />
<DesktopAppList desktopApps={this.props.desktopApps} />
</div>
);
}
});
function select(state) {
return {
desktopApps: state.desktopApps
};
}
module.exports = connect(select)(EditView);

21
js/stores/index.js Normal file
View File

@ -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);

View File

@ -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;
};

View File

@ -0,0 +1,3 @@
exports.desktopApps = require('./desktop-apps');
exports.profile = require('./profile');
exports.processOpts = require('./process-opts');

View File

@ -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;
};

View File

@ -0,0 +1,3 @@
module.exports = function(state, action) {
return {};
};

View File

@ -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"
}
}