diff --git a/js/components/edit/profile-menu.jsx b/js/components/edit/profile-menu.jsx
new file mode 100644
index 0000000..e5a0586
--- /dev/null
+++ b/js/components/edit/profile-menu.jsx
@@ -0,0 +1,79 @@
+var React = require('react');
+var connect = require('react-redux').connect;
+var actions = require('../../store/actions');
+
+var ProfileMenu = React.createClass({
+
+ render: function() {
+
+ return (
+
+
+
+
+
+ );
+
+ },
+
+ handleOpenClick: function() {
+ var dispatch = this.props.dispatch;
+ this.showFileDialog()
+ .then(function(profilePath) {
+ dispatch(actions.common.loadProfile(profilePath));
+ })
+ ;
+ },
+
+ handleSaveClick: function() {
+
+ var dispatch = this.props.dispatch;
+ var profile = this.props.profile;
+ var profilePath = this.props.profilePath;
+
+ var promise = profilePath ? Promise.resolve(profilePath) : this.showFileDialog(true);
+
+ promise.then(function(profilePath) {
+ dispatch(actions.edit.saveProfile(profilePath, profile));
+ });
+
+ },
+
+ showFileDialog: function(saveAs) {
+
+ var fileInput = this.refs.fileInput.getDOMNode();
+
+ // Toggle 'save as' feature
+ if(saveAs) {
+ fileInput.nwsaveas = true;
+ } else {
+ fileInput.removeAttribute('nwsaveas');
+ }
+
+ return new Promise(function(resolve, reject) {
+
+ fileInput.addEventListener('change', handleChange, false);
+ fileInput.click();
+
+ function handleChange(evt) {
+ fileInput.removeEventListener('change', handleChange);
+ var value = this.value;
+ this.value = null;
+ resolve(value);
+ }
+
+ });
+
+
+ }
+
+});
+
+function select(state) {
+ return {
+ profile: state.profile,
+ profilePath: state.profilePath
+ };
+}
+
+module.exports = connect(select)(ProfileMenu);
diff --git a/js/components/edit/profile-tree.jsx b/js/components/edit/profile-tree.jsx
index c4bb491..3e870b7 100644
--- a/js/components/edit/profile-tree.jsx
+++ b/js/components/edit/profile-tree.jsx
@@ -50,10 +50,6 @@ var TreeNode = React.createClass({
var ProfileTree = React.createClass({
- componentDidMount: function() {
- this.props.dispatch(actions.common.loadProfile('./default-profile.json'));
- },
-
render: function() {
return (
diff --git a/js/store/actions/common.js b/js/store/actions/common.js
index edcc659..c3002a9 100644
--- a/js/store/actions/common.js
+++ b/js/store/actions/common.js
@@ -10,7 +10,7 @@ exports.loadProfile = function(profilePath) {
dispatch({ type: LOAD_PROFILE, profilePath: profilePath });
- return Util.System.loadJSONFile(profilePath)
+ return Util.System.loadJSON(profilePath)
.then(function(profile) {
dispatch({ type: LOAD_PROFILE_SUCCESS, profile: profile });
return profile;
diff --git a/js/store/actions/edit.js b/js/store/actions/edit.js
index 1ea1439..a8a88b2 100644
--- a/js/store/actions/edit.js
+++ b/js/store/actions/edit.js
@@ -39,10 +39,19 @@ exports.loadDesktopApps = function() {
};
};
-exports.saveProfile = function(profile) {
+exports.saveProfile = function(destPath, profile) {
return function(dispatch, getState) {
-
+ dispatch({ type: SAVE_PROFILE, profile: profile, path: destPath });
+
+ return Util.System.saveJSON(destPath, profile)
+ .then(function() {
+ dispatch({ type: SAVE_PROFILE_SUCCESS, profile: profile, path: destPath });
+ })
+ .catch(function(err) {
+ dispatch({ type: SAVE_PROFILE_FAILED, error: err });
+ })
+ ;
};
};
diff --git a/js/store/actions/launcher.js b/js/store/actions/launcher.js
index 4c5e131..cf9eded 100644
--- a/js/store/actions/launcher.js
+++ b/js/store/actions/launcher.js
@@ -10,7 +10,7 @@ exports.runApp = function(execPath) {
dispatch({ type: RUN_APP, execPath: execPath });
- return Util.System.runApp(execPath)
+ return Util.System.runApp(execPath, { clearFreeDesktopFlags: true })
.then(function() {
dispatch({ type: RUN_APP_SUCCESS, execPath: execPath });
return execPath;
diff --git a/js/util/system.js b/js/util/system.js
index 92cda9d..0e7688d 100644
--- a/js/util/system.js
+++ b/js/util/system.js
@@ -10,7 +10,7 @@ var Cache = require('./cache');
* @param filePath The path of the json file
* @return Promise
*/
-exports.loadJSONFile = function(filePath) {
+exports.loadJSON = function(filePath) {
return new Promise(function(resolve, reject) {
fs.readFile(filePath, 'utf8', function(err, fileContent) {
if(err) return reject(err);
@@ -24,6 +24,19 @@ exports.loadJSONFile = function(filePath) {
});
};
+exports.saveJSON = function(filePath, obj) {
+
+ var jsonStr = JSON.stringify(obj, null, 2);
+
+ return new Promise(function(resolve, reject) {
+ fs.writeFile(filePath, jsonStr, function(err) {
+ if(err) return reject(err);
+ return resolve();
+ });
+ });
+
+};
+
/**
* Load a INI file
*
@@ -44,7 +57,18 @@ exports.loadINIFile = function(filePath) {
});
};
-exports.runApp = function(execPath) {
+exports.clearFreeDesktopFlags = function(exec) {
+ return exec.replace(/%[uUdDfFnNickvm]/g, '');
+};
+
+exports.runApp = function(execPath, opts) {
+
+ opts = opts || {};
+
+ if(opts.clearFreeDesktopFlags) {
+ execPath = exports.clearFreeDesktopFlags(execPath);
+ }
+
return new Promise(function(resolve, reject) {
cp.exec(execPath, function(err) {
if(err) return reject(err);