desktop plugins

This commit is contained in:
antelle 2017-04-08 17:34:27 +02:00
parent 3766a0e1b5
commit 70cbcb5c36
9 changed files with 145 additions and 18 deletions

View File

@ -3,11 +3,22 @@
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<JSCodeStyleSettings>
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="SPACE_BEFORE_METHOD_PARENTHESES" value="true" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
</codeStyleSettings>
</value>
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>

View File

@ -56,6 +56,9 @@ const Launcher = {
getWorkDirPath: function(fileName) {
return this.req('path').join(process.cwd(), fileName || '');
},
joinPath: function(...parts) {
return this.req('path').join(...parts);
},
writeFile: function(path, data, callback) {
this.req('fs').writeFile(path, new window.Buffer(data), callback);
},

View File

@ -462,6 +462,7 @@
"setPlLoc": "language",
"setPlCreatedBy": "created by {}",
"setPlLoadTime": "took {} to load",
"setPlLoadError": "error loading plugin",
"setAboutTitle": "About",
"setAboutBuilt": "This app is built with these awesome tools",

View File

@ -67,7 +67,7 @@ const PluginManager = Backbone.Model.extend({
const plugin = new Plugin(desc.manifest, desc.url, true);
return plugin.install()
.then(() => plugin)
.catch(() => undefined);
.catch(() => plugin);
},
saveState() {

View File

@ -18,12 +18,20 @@ const io = new IoCache({
const Plugin = Backbone.Model.extend({
idAttribute: 'name',
STATUS_ACTIVE: 'active',
STATUS_INACTIVE: 'inactive',
STATUS_INSTALLING: 'installing',
STATUS_UNINSTALLING: 'uninstalling',
STATUS_INVALID: 'invalid',
STATUS_ERROR: 'error',
defaults: {
name: '',
manifest: '',
url: '',
status: 'inactive',
installTime: null
installTime: null,
installError: null
},
resources: null,
@ -41,16 +49,25 @@ const Plugin = Backbone.Model.extend({
install() {
const ts = this.logger.ts();
this.set('status', 'installing');
this.set('status', this.STATUS_INSTALLING);
return Promise.resolve().then(() => {
const error = this.validateManifest();
if (error) {
this.logger.error('Manifest validation error', error);
this.set('status', 'invalid');
this.set('status', this.STATUS_INVALID);
throw 'Plugin validation error: ' + error;
}
return this.installWithManifest()
.then(() => this.set('installTime', this.logger.ts() - ts));
.then(() => this.set('installTime', this.logger.ts() - ts))
.catch(err => {
this.logger.error('Error installing plugin', err);
this.set({
status: this.STATUS_ERROR,
installError: err,
installTime: this.logger.ts() - ts
});
throw err;
});
});
},
@ -114,6 +131,10 @@ const Plugin = Backbone.Model.extend({
}
},
getStorageResourcePath(res) {
return this.id + '_' + this.getResourcePath(res);
},
loadResource(type) {
let res;
if (this.get('local')) {
@ -156,7 +177,7 @@ const Plugin = Backbone.Model.extend({
loadLocalResource(type) {
return new Promise((resolve, reject) => {
const storageKey = this.id + '/' + this.getResourcePath(type);
const storageKey = this.getStorageResourcePath(type);
io.load(storageKey, (err, data) => {
if (err) {
reject(err);
@ -182,7 +203,7 @@ const Plugin = Backbone.Model.extend({
}
return Promise.all(promises)
.then(() => {
this.set('status', 'active');
this.set('status', this.STATUS_ACTIVE);
})
.catch(e => {
this.logger.info('Install error', e);
@ -204,7 +225,7 @@ const Plugin = Backbone.Model.extend({
saveResource(key, value) {
return new Promise((resolve, reject) => {
const storageKey = this.id + '/' + this.getResourcePath(key);
const storageKey = this.getStorageResourcePath(key);
io.save(storageKey, value, e => {
if (e) {
reject(e);
@ -225,7 +246,7 @@ const Plugin = Backbone.Model.extend({
deleteResource(key) {
return new Promise(resolve => {
const storageKey = this.id + '/' + this.getResourcePath(key);
const storageKey = this.getStorageResourcePath(key);
io.remove(storageKey, () => resolve());
});
},
@ -321,7 +342,7 @@ const Plugin = Backbone.Model.extend({
uninstall() {
const manifest = this.get('manifest');
this.logger.info('Uninstalling plugin with resources', Object.keys(manifest.resources).join(', '));
this.set('status', 'uninstalling');
this.set('status', this.STATUS_UNINSTALLING);
const ts = this.logger.ts();
return Promise.resolve().then(() => {
if (manifest.resources.css) {
@ -342,7 +363,7 @@ const Plugin = Backbone.Model.extend({
this.removeTheme(manifest.theme);
}
return this.deleteResources().then(() => {
this.set('status', 'inactive');
this.set('status', this.STATUS_INACTIVE);
this.logger.info('Uninstall complete', this.logger.ts(ts));
});
});

View File

@ -8,8 +8,6 @@ const IoBrowserCache = function(config) {
this.logger = config.logger;
};
IoBrowserCache.enabled = !!idb;
_.extend(IoBrowserCache.prototype, {
initDb(callback) {
if (this.db) {

View File

@ -2,6 +2,6 @@
const Launcher = require('../comp/launcher');
const IoCache = Launcher ? require('./io-browser-cache') : require('./io-browser-cache'); // TODO: use file cache
const IoCache = Launcher ? require('./io-file-cache') : require('./io-browser-cache');
module.exports = IoCache;

View File

@ -0,0 +1,92 @@
'use strict';
const Launcher = require('../comp/launcher');
const IoFileCache = function(config) {
this.basePath = null;
this.cacheName = config.cacheName;
this.logger = config.logger;
};
_.extend(IoFileCache.prototype, {
initFs(callback) {
if (this.basePath) {
return callback();
}
const basePath = Launcher.getUserDataPath(this.cacheName);
Launcher.mkdir(basePath, err => {
if (err) {
this.logger.error('Error creating plugin folder');
} else {
this.basePath = basePath;
}
callback(err);
});
},
resolvePath(path) {
return Launcher.joinPath(this.basePath, path);
},
save(id, data, callback) {
this.initFs(err => {
if (err) {
return callback && callback(err, null);
}
this.logger.debug('Save', id);
const ts = this.logger.ts();
const path = this.resolvePath(id);
Launcher.writeFile(path, data, err => {
if (err) {
this.logger.error('Error saving file', id, err);
if (callback) { callback(err); }
} else {
this.logger.debug('Saved', id, this.logger.ts(ts));
if (callback) { callback(); }
}
});
});
},
load(id, callback) {
this.initFs(err => {
if (err) {
return callback && callback(err, null);
}
this.logger.debug('Load', id);
const ts = this.logger.ts();
const path = this.resolvePath(id);
Launcher.readFile(path, undefined, (data, err) => {
if (err) {
this.logger.error('Error loading file', id, err);
if (callback) { callback(err); }
} else {
this.logger.debug('Loaded', id, this.logger.ts(ts));
if (callback) { callback(null, data); }
}
});
});
},
remove(id, callback) {
this.initFs(err => {
if (err) {
return callback && callback(err, null);
}
this.logger.debug('Remove', id);
const ts = this.logger.ts();
const path = this.resolvePath(id);
Launcher.deleteFile(path, err => {
if (err) {
this.logger.error('Error removing file', id, err);
if (callback) { callback(err); }
} else {
this.logger.debug('Removed', id, this.logger.ts(ts));
if (callback) { callback(); }
}
});
});
}
});
module.exports = IoFileCache;

View File

@ -16,6 +16,7 @@
<a href="{{plugin.manifest.url}}" target="_blank">{{plugin.manifest.url}}</a>, v{{plugin.manifest.version}},
{{#res 'setPlCreatedBy'}}<a href="{{plugin.manifest.author.url}}" target="_blank">{{plugin.manifest.author.name}}</a> ({{plugin.manifest.author.email}}){{/res}},
{{#res 'setPlLoadTime'}}{{plugin.installTime}}ms{{/res}}
{{#ifeq plugin.status 'error'}}<span class="error-color">&nbsp;({{res 'setPlLoadError'}}){{/ifeq}}
</div>
<div class="settings__plugins-plugin-buttons">
<button class="settings_plugins-uninstall-btn btn-silent" data-plugin="{{plugin.id}}">{{res 'setPlUninstallBtn'}}</button>