From 747f3519e07c0b1d494c117ea6854d2de6700f91 Mon Sep 17 00:00:00 2001 From: William Petit Date: Tue, 13 Oct 2015 14:32:22 +0200 Subject: [PATCH 1/4] =?UTF-8?q?Chargement=20s=C3=A9quentiel=20des=20applic?= =?UTF-8?q?ations/icones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/components/edit/edit-view.js | 4 +--- js/components/edit/profile-menu.js | 4 ++-- js/components/edit/profile-tree.js | 1 + js/util/desktop-apps.js | 35 ++++++++++++------------------ js/util/promises.js | 25 +++++++++++++++++++++ 5 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 js/util/promises.js diff --git a/js/components/edit/edit-view.js b/js/components/edit/edit-view.js index eb29ffc..f7a89c7 100644 --- a/js/components/edit/edit-view.js +++ b/js/components/edit/edit-view.js @@ -21,11 +21,9 @@ var EditView = React.createClass({ return (
-
- -
+ - - + +
); diff --git a/js/components/edit/profile-tree.js b/js/components/edit/profile-tree.js index 98f867e..b785acf 100644 --- a/js/components/edit/profile-tree.js +++ b/js/components/edit/profile-tree.js @@ -55,6 +55,7 @@ var ProfileTree = React.createClass({ return (
+ {this.renderTreeNode(this.props.profile)}
); diff --git a/js/util/desktop-apps.js b/js/util/desktop-apps.js index 1b0f0f6..fbb03fe 100644 --- a/js/util/desktop-apps.js +++ b/js/util/desktop-apps.js @@ -2,6 +2,7 @@ var path = require('path'); var System = require('./system'); var debug = require('./debug')('desktop-apps'); var Cache = require('./cache'); +var promises = require('./promises'); // Constants var ICON_REALPATH_REGEX = /\..+$/; @@ -19,11 +20,9 @@ exports.loadAllDesktopFiles = function(rootDirs) { return exports.findAllDesktopFiles(rootDirs) .then(function(filePaths) { - var promises = filePaths.map(function(path) { - return exports.loadDesktopFile(path); - }); - - return Promise.all(promises) + return promises.seq(filePaths, function(path) { + return exports.loadDesktopFile(path); + }) .then(function(contents) { return contents.map(function(content, i) { return { content: content, path: filePaths[i] }; @@ -47,12 +46,10 @@ exports.findAllDesktopFiles = function(baseDirs) { if(!Array.isArray(baseDirs)) { baseDirs = [baseDirs]; } - - var promises = baseDirs.map(function(baseDir) { - return System.findFiles('**/*.desktop', {cwd: baseDir, realpath: true}); - }); - - return Promise.all(promises) + + return promises.seq(baseDirs, function(baseDir) { + return System.findFiles('**/*.desktop', {cwd: baseDir, realpath: true}); + }) .then(function(apps) { return uniq(flatten(apps)); }) @@ -104,10 +101,9 @@ exports.findIcon = function(iconName, themeName, size, themeIgnore) { return exports.findIconThemes() .then(function(themes) { themeIgnore = themeIgnore || []; - var promises = themes.map(function(theme) { - return exports.findIcon(iconName, theme, size, themeIgnore); - }); - return Promise.all(promises) + return promises.seq(themes, function(theme) { + return exports.findIcon(iconName, theme, size, themeIgnore); + }) .then(exports._selectBestIcon) ; }) @@ -160,11 +156,9 @@ exports.findParentsThemeIcon = function(iconName, themeName, size, themeIgnore) debug('Found parents %j', parents); - var promises = parents.map(function(themeName) { - return exports.findIcon(iconName, themeName, size, themeIgnore); - }); - - return Promise.all(promises) + return promises.seq(parents, function(themeName) { + return exports.findIcon(iconName, themeName, size, themeIgnore); + }) .then(exports._selectBestIcon) ; @@ -278,7 +272,6 @@ exports._selectBestIcon = function(iconPaths) { return iconSelection.scalable || iconSelection.bitmap; }; - // Array helpers function clean(arr) { diff --git a/js/util/promises.js b/js/util/promises.js new file mode 100644 index 0000000..f7ddfda --- /dev/null +++ b/js/util/promises.js @@ -0,0 +1,25 @@ + + +exports.seq = function(items, generator) { + + var results = []; + var p = Promise.resolve(); + + for(var i = 0, len = items.length; i < len; ++i) { + p = p.then(generateNextHandler(items[i], i === 0)); + } + + return p.then(function(lastResult) { + results.push(lastResult); + return results; + }); + + // Internal helper + function generateNextHandler(item, ignoreResult) { + return function(prevResult) { + if(!ignoreResult) results.push(prevResult); + return generator(item, prevResult); + }; + } + +}; From 740977ed791acbb433ce4c3655808171668a514f Mon Sep 17 00:00:00 2001 From: William Petit Date: Tue, 13 Oct 2015 17:27:15 +0200 Subject: [PATCH 2/4] Base new layout --- css/style.css | 29 +++++++++++++++++++-------- js/components/edit/edit-view.js | 32 +++++++++++++++++++++--------- js/components/edit/profile-tree.js | 1 - 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/css/style.css b/css/style.css index a2cc2c4..17dccec 100644 --- a/css/style.css +++ b/css/style.css @@ -149,17 +149,30 @@ html, body { .edit .menu-bar { padding: 5px 10px; + display: flex; + flex-direction: row; } .edit .menu-bar button { margin-right: 3px; } +.edit .left { + flex: 2; +} + +.edit .main { + flex: 3; +} + +.edit .right { + flex: 1; +} + .edit .workspace { display: flex; flex-direction: row; padding: 10px; - flex: 3; } .edit .left-menu { @@ -169,6 +182,11 @@ html, body { overflow-y: auto; } +.edit .workspace .left .apps-menu { + display: flex; + flex-direction: column; +} + .edit .item-form { display: flex; flex-direction: row; @@ -188,11 +206,8 @@ html, body { padding: 0; height: 100%; margin: 10px 0 0 0; - padding: 0 10px 0 0; -} - -.edit .apps-list li.desktop-app { - + overflow-y: auto; + display: block; } .edit .desktop-app > .app-icon { @@ -204,7 +219,6 @@ html, body { } .edit .profile-tree { - flex: 3; padding: 0 5px; } @@ -228,7 +242,6 @@ html, body { } .edit .app-item-edit { - flex: 1; padding: 0 5px; } diff --git a/js/components/edit/edit-view.js b/js/components/edit/edit-view.js index f7a89c7..e252aa3 100644 --- a/js/components/edit/edit-view.js +++ b/js/components/edit/edit-view.js @@ -21,17 +21,31 @@ var EditView = React.createClass({ return (
-
-
+
+
- -
- - +
+ +
+
+
+
+
+
+ + +
+
+
+ +
+
+ +
); diff --git a/js/components/edit/profile-tree.js b/js/components/edit/profile-tree.js index b785acf..98f867e 100644 --- a/js/components/edit/profile-tree.js +++ b/js/components/edit/profile-tree.js @@ -55,7 +55,6 @@ var ProfileTree = React.createClass({ return (
- {this.renderTreeNode(this.props.profile)}
); From 39399a5c082e66baeb8e570b56af4a2abd6e9d08 Mon Sep 17 00:00:00 2001 From: William Petit Date: Tue, 13 Oct 2015 20:44:55 +0200 Subject: [PATCH 3/4] Meilleur agencement --- css/style.css | 40 ++++++++++++++++++++++++++++----- default-profile.json | 14 +++++++++--- js/components/edit/edit-view.js | 8 ++++++- js/components/edit/item-form.js | 15 ++++++++----- js/util/promises.js | 8 ++++++- main.js | 2 ++ 6 files changed, 71 insertions(+), 16 deletions(-) diff --git a/css/style.css b/css/style.css index 17dccec..9e3d298 100644 --- a/css/style.css +++ b/css/style.css @@ -20,6 +20,10 @@ html, body { border: 1px solid #ddd; } +.full-width { + width: 100%; +} + /* Launcher View */ .launcher { @@ -147,6 +151,10 @@ html, body { flex-direction: column; } +.edit .title { + margin-top: 5px; +} + .edit .menu-bar { padding: 5px 10px; display: flex; @@ -158,23 +166,32 @@ html, body { } .edit .left { - flex: 2; + flex: 1; + display: flex; } .edit .main { flex: 3; + display: flex; } .edit .right { flex: 1; + display: flex; } .edit .workspace { display: flex; flex-direction: row; padding: 10px; + flex: 1; } +.edit .workspace .main { + flex-direction: column; +} + + .edit .left-menu { display: flex; flex-direction: column; @@ -187,6 +204,10 @@ html, body { flex-direction: column; } +.edit .workspace .right { + flex-direction: column; +} + .edit .item-form { display: flex; flex-direction: row; @@ -197,6 +218,11 @@ html, body { width: 100%; } +.edit .apps-list { + overflow: auto; + margin-top: 5px; +} + .edit .apps-list .icon-theme-selector > select { width: 100%; } @@ -205,21 +231,25 @@ html, body { list-style: none; padding: 0; height: 100%; - margin: 10px 0 0 0; - overflow-y: auto; display: block; + margin-right: 5px; } .edit .desktop-app > .app-icon { - height: 35px; - width: 35px; + height: 20px; + width: auto; display: inline-block; vertical-align: middle; margin-right: 10px; } +.edit .desktop-app.list-group-item { + padding: 5px 10px; +} + .edit .profile-tree { padding: 0 5px; + width: 100%; } .edit .profile-tree ul { diff --git a/default-profile.json b/default-profile.json index f9779d0..4bf6228 100644 --- a/default-profile.json +++ b/default-profile.json @@ -19,7 +19,7 @@ "_key": "item_1444480285022_2" }, { - "label": "Level 2-2", + "label": "Level 2-3", "icon": "chromium-browser", "items": [ { @@ -36,7 +36,15 @@ "label": "Atom", "icon": "atom", "exec": "/usr/share/atom/atom %U", - "_key": "item_1444480288996_7" + "_key": "item_1444480288996_7", + "items": [] + }, + { + "label": "Firefox Developer Edition Web Browser", + "icon": "firefox", + "exec": "firefox %u", + "_key": "item_1444761351301_2", + "selected": true } ], "_key": "item_1444480285022_5" @@ -49,4 +57,4 @@ } ], "_key": "item_1444480285021_0" -} +} \ No newline at end of file diff --git a/js/components/edit/edit-view.js b/js/components/edit/edit-view.js index e252aa3..c8a7074 100644 --- a/js/components/edit/edit-view.js +++ b/js/components/edit/edit-view.js @@ -26,14 +26,18 @@ var EditView = React.createClass({
- +
+ +
+ Thème + Applications
+ Arbre de profil
+ Édition
diff --git a/js/components/edit/item-form.js b/js/components/edit/item-form.js index 58933a9..874b38e 100644 --- a/js/components/edit/item-form.js +++ b/js/components/edit/item-form.js @@ -17,7 +17,8 @@ var ItemForm = React.createClass({ this.setState({ label: props.item.label, icon: props.item.icon, - exec: props.item.exec + exec: props.item.exec, + background: props.item.background }); } @@ -31,25 +32,27 @@ var ItemForm = React.createClass({
-
-
-
+
+ +
); diff --git a/js/util/promises.js b/js/util/promises.js index f7ddfda..07ded5c 100644 --- a/js/util/promises.js +++ b/js/util/promises.js @@ -6,7 +6,7 @@ exports.seq = function(items, generator) { var p = Promise.resolve(); for(var i = 0, len = items.length; i < len; ++i) { - p = p.then(generateNextHandler(items[i], i === 0)); + p = p.then(generateNextHandler(items[i], i === 0)) } return p.then(function(lastResult) { @@ -23,3 +23,9 @@ exports.seq = function(items, generator) { } }; + +exports.delay = function(delay) { + return new Promise(function(resolve) { + setTimeout(resolve, delay); + }); +}; diff --git a/main.js b/main.js index edd65bb..0e16d00 100644 --- a/main.js +++ b/main.js @@ -1,5 +1,6 @@ var app = require('app'); // Module to control application life. var BrowserWindow = require('browser-window'); // Module to create native browser window. +var Menu = require('menu'); var isDev = process.env.NODE_ENV === 'development'; var constants = require('./'+(isDev ? 'js': 'js-compiled')+'/util/const'); @@ -23,6 +24,7 @@ app.on('ready', function() { frame: !asDesktop, width: asDesktop ? size.width : undefined, height: asDesktop ? size.height : undefined, + 'auto-hide-menu-bar': true, x: asDesktop ? 0 : undefined, y: asDesktop ? 0 : undefined, }); From 2d8f9bd9035f5d31cf37afc75ec9387b930a3bdd Mon Sep 17 00:00:00 2001 From: William Petit Date: Fri, 16 Oct 2015 15:12:49 +0200 Subject: [PATCH 4/4] Ajout police Sawaasdee, rename js to src --- css/style.css | 43 ++++++++++++++---- fonts/sawasdee-webfont.woff2 | Bin 0 -> 31620 bytes index.html | 8 ++-- main.js | 6 +-- package.json | 6 +-- {js => src}/app.js | 0 {js => src}/components/common/app-icon.js | 13 +++++- .../components/edit/desktop-app-item.js | 0 .../components/edit/desktop-app-list.js | 0 {js => src}/components/edit/edit-view.js | 11 +++++ .../components/edit/icon-theme-selector.js | 0 {js => src}/components/edit/item-form.js | 0 {js => src}/components/edit/profile-menu.js | 0 {js => src}/components/edit/profile-tree.js | 0 {js => src}/components/edit/tree-item.js | 0 {js => src}/components/launcher/app-item.js | 0 {js => src}/components/launcher/app-list.js | 0 .../components/launcher/category-header.js | 0 .../components/launcher/launcher-view.js | 0 {js => src}/components/launcher/nav.js | 0 {js => src}/components/mixins/animate.js | 0 {js => src}/components/mixins/lazy-load.js | 0 {js => src}/store/actions/common.js | 0 {js => src}/store/actions/edit.js | 10 +++- {js => src}/store/actions/index.js | 0 {js => src}/store/actions/launcher.js | 0 {js => src}/store/index.js | 0 {js => src}/store/middlewares/logger.js | 0 {js => src}/store/reducers/desktop-apps.js | 0 {js => src}/store/reducers/index.js | 0 {js => src}/store/reducers/profile.js | 29 +++++++----- {js => src}/store/reducers/theme.js | 0 {js => src}/util/cache.js | 0 {js => src}/util/const.js | 0 {js => src}/util/debug.js | 0 {js => src}/util/desktop-apps.js | 2 +- {js => src}/util/index.js | 3 ++ {js => src}/util/promises.js | 0 {js => src}/util/system.js | 0 {js => src}/util/tree.js | 0 40 files changed, 97 insertions(+), 34 deletions(-) create mode 100644 fonts/sawasdee-webfont.woff2 rename {js => src}/app.js (100%) rename {js => src}/components/common/app-icon.js (82%) rename {js => src}/components/edit/desktop-app-item.js (100%) rename {js => src}/components/edit/desktop-app-list.js (100%) rename {js => src}/components/edit/edit-view.js (89%) rename {js => src}/components/edit/icon-theme-selector.js (100%) rename {js => src}/components/edit/item-form.js (100%) rename {js => src}/components/edit/profile-menu.js (100%) rename {js => src}/components/edit/profile-tree.js (100%) rename {js => src}/components/edit/tree-item.js (100%) rename {js => src}/components/launcher/app-item.js (100%) rename {js => src}/components/launcher/app-list.js (100%) rename {js => src}/components/launcher/category-header.js (100%) rename {js => src}/components/launcher/launcher-view.js (100%) rename {js => src}/components/launcher/nav.js (100%) rename {js => src}/components/mixins/animate.js (100%) rename {js => src}/components/mixins/lazy-load.js (100%) rename {js => src}/store/actions/common.js (100%) rename {js => src}/store/actions/edit.js (91%) rename {js => src}/store/actions/index.js (100%) rename {js => src}/store/actions/launcher.js (100%) rename {js => src}/store/index.js (100%) rename {js => src}/store/middlewares/logger.js (100%) rename {js => src}/store/reducers/desktop-apps.js (100%) rename {js => src}/store/reducers/index.js (100%) rename {js => src}/store/reducers/profile.js (77%) rename {js => src}/store/reducers/theme.js (100%) rename {js => src}/util/cache.js (100%) rename {js => src}/util/const.js (100%) rename {js => src}/util/debug.js (100%) rename {js => src}/util/desktop-apps.js (99%) rename {js => src}/util/index.js (58%) rename {js => src}/util/promises.js (100%) rename {js => src}/util/system.js (100%) rename {js => src}/util/tree.js (100%) diff --git a/css/style.css b/css/style.css index 9e3d298..644074d 100644 --- a/css/style.css +++ b/css/style.css @@ -1,3 +1,10 @@ +@font-face { + font-family: 'sawasdeeregular'; + src: url('../fonts/sawasdee-webfont.woff2') format('woff2'); + font-weight: normal; + font-style: normal; +} + * { box-sizing: border-box; } @@ -5,7 +12,7 @@ html, body { padding: 0; margin: 0; - font-family: 'Droid Sans', 'Ubuntu Sans', sans-serif; + font-family: 'sawasdeeregular'; background-size: contain; background-position: center; background-color: rgb(34, 107, 160); @@ -15,11 +22,21 @@ html, body { overflow-x: hidden; } + +/* Common */ + .alert.alert-default { border-radius: 4px; border: 1px solid #ddd; } +.app-icon { + background-position: center center; + background-size: contain; + background-repeat: no-repeat; + transition: background-image 250ms ease-in-out; +} + .full-width { width: 100%; } @@ -28,14 +45,17 @@ html, body { .launcher { display: flex; - width: 100%; - height: 100%; flex-direction: column; background: url('../img/background.png'); background-repeat: no-repeat; background-size: cover; background-position: center center; transition: background-image 250ms ease-in-out; + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; } .launcher .main { @@ -48,7 +68,7 @@ html, body { justify-content: center; align-items: center; display: flex; - width: 50px; + width: 60px; } .launcher .nav a.goback { @@ -56,6 +76,7 @@ html, body { color: white; font-size: 60px; text-shadow: 1px 1px #444; + font-family: sans-serif; } .launcher .nav a.goback:hover { @@ -79,7 +100,7 @@ html, body { .launcher ul.apps-list { margin: 0; - padding: 0; + padding: 0 20%; display: flex; flex-direction: row; list-style: none; @@ -91,7 +112,7 @@ html, body { } .launcher .nav ~ ul.apps-list { - margin-left: -50px; + margin-left: -60px; } .launcher li.app-item { @@ -127,8 +148,9 @@ html, body { } .launcher li.app-item > .app-icon { - width: 70%; - height: auto; + width: 90px; + height: 90px; + margin: auto; } .launcher li.app-item > .app-label { @@ -237,7 +259,7 @@ html, body { .edit .desktop-app > .app-icon { height: 20px; - width: auto; + width: 20px; display: inline-block; vertical-align: middle; margin-right: 10px; @@ -268,7 +290,10 @@ html, body { .edit .profile-tree .tree-item .app-icon { height: 25px; + width: 25px; margin-right: 5px; + display: inline-block; + vertical-align: middle; } .edit .app-item-edit { diff --git a/fonts/sawasdee-webfont.woff2 b/fonts/sawasdee-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9903526421f68b3d6a61ee125ef04988f5394b51 GIT binary patch literal 31620 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 T>6R(gV%Eb`M`ZfNe-8lwWcp Lanceur - Pitaya - @@ -20,12 +19,11 @@ if(isDev) { // Auto transform JSX require('node-jsx').install({extension: '.js'}); - // Launch application - require('./js/app.js'); - } else { - require('./js-compiled/app.js'); } + // Launch app + require('./'+(isDev ? 'src' : 'dist')+'/app.js'); + diff --git a/main.js b/main.js index 0e16d00..ee5310c 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,7 @@ var app = require('app'); // Module to control application life. var BrowserWindow = require('browser-window'); // Module to create native browser window. var Menu = require('menu'); var isDev = process.env.NODE_ENV === 'development'; -var constants = require('./'+(isDev ? 'js': 'js-compiled')+'/util/const'); +var constants = require('./'+(isDev ? 'src': 'dist')+'/util/const'); var mainWindow = null; @@ -14,7 +14,7 @@ app.on('window-all-closed', function() { app.on('ready', function() { // Create the browser window. var electronScreen = require('screen'); - var size = electronScreen.getPrimaryDisplay().workAreaSize; + var size = electronScreen.getPrimaryDisplay().size; var asDesktop = process.env.PITAYA_AS_DESKTOP == 1; @@ -29,7 +29,7 @@ app.on('ready', function() { y: asDesktop ? 0 : undefined, }); - if(process.env.NODE_ENV === 'development') { + if(isDev) { mainWindow.openDevTools(); } diff --git a/package.json b/package.json index 3db5826..99c47d7 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "scripts": { "test": "./node_modules/.bin/nodeunit test", "start": "./node_modules/.bin/electron .", - "compile": "./node_modules/.bin/jsx -x js js js-compiled", - "clean": "rm -rf js-compiled/* build/*", - "package": "./node_modules/.bin/electron-packager ./ pitaya --prune --ignore=js/ --platform=linux --arch=ia32 --version=0.33.6 --out=build --overwrite --app-version 0.0.0", + "compile": "./node_modules/.bin/jsx -x js src dist", + "clean": "rm -rf dist/* build/*", + "package": "./node_modules/.bin/electron-packager ./ pitaya --prune --ignore=res/ --platform=linux --arch=ia32,x64 --version=0.33.6 --out=build --overwrite --app-version 0.0.0", "build": "npm run clean && npm run compile && npm run package" }, "dependencies": { diff --git a/js/app.js b/src/app.js similarity index 100% rename from js/app.js rename to src/app.js diff --git a/js/components/common/app-icon.js b/src/components/common/app-icon.js similarity index 82% rename from js/components/common/app-icon.js rename to src/components/common/app-icon.js index 53c4388..f67770e 100644 --- a/js/components/common/app-icon.js +++ b/src/components/common/app-icon.js @@ -41,8 +41,12 @@ module.exports = React.createClass({ var icon = this.state.icon; + var style = { + backgroundImage: 'url('+icon+')' + }; + return ( - +
); }, @@ -60,6 +64,13 @@ module.exports = React.createClass({ } return iconPath; }) + .then(function(iconPath) { + return Util.System.exists(iconPath) + .then(function(exists) { + return exists ? iconPath : DEFAULT_ICON; + }) + ; + }) .then(function(iconPath) { self.setState({ icon: iconPath }); }) diff --git a/js/components/edit/desktop-app-item.js b/src/components/edit/desktop-app-item.js similarity index 100% rename from js/components/edit/desktop-app-item.js rename to src/components/edit/desktop-app-item.js diff --git a/js/components/edit/desktop-app-list.js b/src/components/edit/desktop-app-list.js similarity index 100% rename from js/components/edit/desktop-app-list.js rename to src/components/edit/desktop-app-list.js diff --git a/js/components/edit/edit-view.js b/src/components/edit/edit-view.js similarity index 89% rename from js/components/edit/edit-view.js rename to src/components/edit/edit-view.js index c8a7074..551d300 100644 --- a/js/components/edit/edit-view.js +++ b/src/components/edit/edit-view.js @@ -21,6 +21,7 @@ var EditView = React.createClass({ return (
+
@@ -76,6 +77,16 @@ var EditView = React.createClass({ handleItemChange: function(item, key, value) { this.props.dispatch(actions.edit.updateProfileItem(item, key, value)); + }, + + handleAddNewNode: function() { + var newItem = { + label: 'Nouveau noeud', + icon: '', + exec: '', + background: '' + }; + this.props.dispatch(actions.edit.addProfileItem(newItem, this.props.profile)); } }); diff --git a/js/components/edit/icon-theme-selector.js b/src/components/edit/icon-theme-selector.js similarity index 100% rename from js/components/edit/icon-theme-selector.js rename to src/components/edit/icon-theme-selector.js diff --git a/js/components/edit/item-form.js b/src/components/edit/item-form.js similarity index 100% rename from js/components/edit/item-form.js rename to src/components/edit/item-form.js diff --git a/js/components/edit/profile-menu.js b/src/components/edit/profile-menu.js similarity index 100% rename from js/components/edit/profile-menu.js rename to src/components/edit/profile-menu.js diff --git a/js/components/edit/profile-tree.js b/src/components/edit/profile-tree.js similarity index 100% rename from js/components/edit/profile-tree.js rename to src/components/edit/profile-tree.js diff --git a/js/components/edit/tree-item.js b/src/components/edit/tree-item.js similarity index 100% rename from js/components/edit/tree-item.js rename to src/components/edit/tree-item.js diff --git a/js/components/launcher/app-item.js b/src/components/launcher/app-item.js similarity index 100% rename from js/components/launcher/app-item.js rename to src/components/launcher/app-item.js diff --git a/js/components/launcher/app-list.js b/src/components/launcher/app-list.js similarity index 100% rename from js/components/launcher/app-list.js rename to src/components/launcher/app-list.js diff --git a/js/components/launcher/category-header.js b/src/components/launcher/category-header.js similarity index 100% rename from js/components/launcher/category-header.js rename to src/components/launcher/category-header.js diff --git a/js/components/launcher/launcher-view.js b/src/components/launcher/launcher-view.js similarity index 100% rename from js/components/launcher/launcher-view.js rename to src/components/launcher/launcher-view.js diff --git a/js/components/launcher/nav.js b/src/components/launcher/nav.js similarity index 100% rename from js/components/launcher/nav.js rename to src/components/launcher/nav.js diff --git a/js/components/mixins/animate.js b/src/components/mixins/animate.js similarity index 100% rename from js/components/mixins/animate.js rename to src/components/mixins/animate.js diff --git a/js/components/mixins/lazy-load.js b/src/components/mixins/lazy-load.js similarity index 100% rename from js/components/mixins/lazy-load.js rename to src/components/mixins/lazy-load.js diff --git a/js/store/actions/common.js b/src/store/actions/common.js similarity index 100% rename from js/store/actions/common.js rename to src/store/actions/common.js diff --git a/js/store/actions/edit.js b/src/store/actions/edit.js similarity index 91% rename from js/store/actions/edit.js rename to src/store/actions/edit.js index 3bc664e..5f41925 100644 --- a/js/store/actions/edit.js +++ b/src/store/actions/edit.js @@ -1,5 +1,6 @@ var Util = require('../../util'); var path = require('path'); +var _ = require('lodash'); // Action types var LOAD_DESKTOP_APPS = exports.LOAD_PROFILE = 'LOAD_DESKTOP_APPS'; @@ -45,7 +46,14 @@ exports.saveProfile = function(destPath, profile) { dispatch({ type: SAVE_PROFILE, profile: profile, path: destPath }); - return Util.System.saveJSON(destPath, profile) + var cleanedProfile = _.cloneDeep(profile); + + Util.Tree.walk(cleanedProfile, function(item) { + delete item.selected; + delete item._key; + }); + + return Util.System.saveJSON(destPath, cleanedProfile) .then(function() { dispatch({ type: SAVE_PROFILE_SUCCESS, profile: profile, path: destPath }); }) diff --git a/js/store/actions/index.js b/src/store/actions/index.js similarity index 100% rename from js/store/actions/index.js rename to src/store/actions/index.js diff --git a/js/store/actions/launcher.js b/src/store/actions/launcher.js similarity index 100% rename from js/store/actions/launcher.js rename to src/store/actions/launcher.js diff --git a/js/store/index.js b/src/store/index.js similarity index 100% rename from js/store/index.js rename to src/store/index.js diff --git a/js/store/middlewares/logger.js b/src/store/middlewares/logger.js similarity index 100% rename from js/store/middlewares/logger.js rename to src/store/middlewares/logger.js diff --git a/js/store/reducers/desktop-apps.js b/src/store/reducers/desktop-apps.js similarity index 100% rename from js/store/reducers/desktop-apps.js rename to src/store/reducers/desktop-apps.js diff --git a/js/store/reducers/index.js b/src/store/reducers/index.js similarity index 100% rename from js/store/reducers/index.js rename to src/store/reducers/index.js diff --git a/js/store/reducers/profile.js b/src/store/reducers/profile.js similarity index 77% rename from js/store/reducers/profile.js rename to src/store/reducers/profile.js index af735f1..454a82d 100644 --- a/js/store/reducers/profile.js +++ b/src/store/reducers/profile.js @@ -4,33 +4,40 @@ var tree = require('../../util/tree'); module.exports = function(oldProfile, action) { + var newProfile = oldProfile || { items: [] }; + switch(action.type) { case actions.common.LOAD_PROFILE_SUCCESS: - var newProfile = _.cloneDeep(action.profile); - tree.walk(newProfile, ensureItemKey); - return newProfile; + newProfile = _.cloneDeep(action.profile); + break; case actions.edit.MOVE_PROFILE_ITEM: - return moveProfileItem(oldProfile, action.movedItem, action.targetItem); + newProfile = moveProfileItem(oldProfile, action.movedItem, action.targetItem); + break; case actions.edit.REMOVE_PROFILE_ITEM: - return removeProfileItem(oldProfile, action.removedItem); + newProfile = removeProfileItem(oldProfile, action.removedItem); + break; case actions.edit.ADD_PROFILE_ITEM: - return addProfileItem(oldProfile, action.newItem, action.targetItem); + newProfile = addProfileItem(oldProfile, action.newItem, action.targetItem); + break; case actions.edit.UPDATE_PROFILE_ITEM: - return updateProfileItem(oldProfile, action.item, action.key, action.value); + newProfile = updateProfileItem(oldProfile, action.item, action.key, action.value); + break; case actions.edit.SELECT_PROFILE_ITEM: - return selectProfileItem(oldProfile, action.item); - - default: - return oldProfile || null; + newProfile = selectProfileItem(oldProfile, action.item); + break; } + if(newProfile) tree.walk(newProfile, ensureItemKey); + + return newProfile; + }; function selectProfileItem(oldProfile, item) { diff --git a/js/store/reducers/theme.js b/src/store/reducers/theme.js similarity index 100% rename from js/store/reducers/theme.js rename to src/store/reducers/theme.js diff --git a/js/util/cache.js b/src/util/cache.js similarity index 100% rename from js/util/cache.js rename to src/util/cache.js diff --git a/js/util/const.js b/src/util/const.js similarity index 100% rename from js/util/const.js rename to src/util/const.js diff --git a/js/util/debug.js b/src/util/debug.js similarity index 100% rename from js/util/debug.js rename to src/util/debug.js diff --git a/js/util/desktop-apps.js b/src/util/desktop-apps.js similarity index 99% rename from js/util/desktop-apps.js rename to src/util/desktop-apps.js index fbb03fe..3645c9c 100644 --- a/js/util/desktop-apps.js +++ b/src/util/desktop-apps.js @@ -46,7 +46,7 @@ exports.findAllDesktopFiles = function(baseDirs) { if(!Array.isArray(baseDirs)) { baseDirs = [baseDirs]; } - + return promises.seq(baseDirs, function(baseDir) { return System.findFiles('**/*.desktop', {cwd: baseDir, realpath: true}); }) diff --git a/js/util/index.js b/src/util/index.js similarity index 58% rename from js/util/index.js rename to src/util/index.js index 49a2fea..9070008 100644 --- a/js/util/index.js +++ b/src/util/index.js @@ -2,3 +2,6 @@ exports.System = require('./system'); exports.DesktopApps = require('./desktop-apps'); exports.Cache = require('./cache'); exports.Debug = require('./debug'); +exports.Tree = require('./tree'); +exports.Const = require('./const'); +exports.Promises = require('./promises'); diff --git a/js/util/promises.js b/src/util/promises.js similarity index 100% rename from js/util/promises.js rename to src/util/promises.js diff --git a/js/util/system.js b/src/util/system.js similarity index 100% rename from js/util/system.js rename to src/util/system.js diff --git a/js/util/tree.js b/src/util/tree.js similarity index 100% rename from js/util/tree.js rename to src/util/tree.js