diff --git a/README.md b/README.md index ac80b8270..20ae8afb2 100644 --- a/README.md +++ b/README.md @@ -377,40 +377,37 @@ that handles configuring the app. When the app settings are opened, this function is called with one argument, `back`: a callback to return to the settings menu. -Usually it will save any information in `app.json` where `app` is the name +Usually it will save any information in `myappid.json` where `myappid` is the name of your app - so you should change the example accordingly. Example `settings.js` ```js // make sure to enclose the function in parentheses (function(back) { - let settings = require('Storage').readJSON('app.json',1)||{}; - function save(key, value) { - settings[key] = value; - require('Storage').write('app.json',settings); - } + function get(key, def) { return require('Settings').get('myappid', key, def); } + function set(key, value) { require('Settings').set('myappid', key, value); } const appMenu = { '': {'title': 'App Settings'}, '< Back': back, 'Monkeys': { - value: settings.monkeys||12, - onchange: (m) => {save('monkeys', m)} + value: get('monkeys', 12), + onchange: (m) => set('monkeys', m) } }; E.showMenu(appMenu) }) ``` -In this example the app needs to add `app.settings.js` to `storage` in `apps.json`. -It should also add `app.json` to `data`, to make sure it is cleaned up when the app is uninstalled. +In this example the app needs to add `myappid.settings.js` to `storage` in `apps.json`. +It should also add `myappid.json` to `data`, to make sure it is cleaned up when the app is uninstalled. ```json - { "id": "app", + { "id": "myappid", ... "storage": [ ... - {"name":"app.settings.js","url":"settings.js"}, + {"name":"myappid.settings.js","url":"settings.js"} ], "data": [ - {"name":"app.json"} + {"name":"myappid.json"} ] }, ``` diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js index a84d26efd..ea45dc19b 100755 --- a/bin/sanitycheck.js +++ b/bin/sanitycheck.js @@ -209,6 +209,8 @@ apps.forEach((app,appIdx) => { // prefer "appid.json" over "appid.settings.json" (TODO: change to ERROR once all apps comply?) if (dataNames.includes(app.id+".settings.json") && !dataNames.includes(app.id+".json")) WARN(`App ${app.id} uses data file ${app.id+'.settings.json'} instead of ${app.id+'.json'}`) + else if (dataNames.includes(app.id+".settings.json")) + WARN(`App ${app.id} uses data file ${app.id+'.settings.json'}`) // settings files should be listed under data, not storage (TODO: change to ERROR once all apps comply?) if (fileNames.includes(app.id+".settings.json")) WARN(`App ${app.id} uses storage file ${app.id+'.settings.json'} instead of data file`) diff --git a/modules/Settings.js b/modules/Settings.js new file mode 100644 index 000000000..8d7fba653 --- /dev/null +++ b/modules/Settings.js @@ -0,0 +1,101 @@ +/* +- Read/write app settings, stored in .json +- Read/write global settings (stored in setting.json) + +Usage: +``` +// read a single app setting +value = require('Settings').get(appid, key, default); +// omit key to read all app settings +value = require('Settings').get(); +// write a single app setting +require('Settings').set(appid, key, value) +// omit key and pass an object as values to overwrite all settings +require('Settings').set(appid, values) + +// read Bangle settings by passing the Bangle object instead of an app name +value = require('Settings').get(Bangle, key, default); +// read all global settings +values = require('Settings').get(Bangle); +// write a global setting +require('Settings').set(Bangle, key, value) +``` + +For example: +``` +require('Settings').set('test', 'foo', 123); // writes to 'test.json' +require('Settings').set('test', 'bar', 456); // updates 'test.json' +// 'test.json' now contains {baz:123,bam:456} +baz = require('Settings').get('test', 'foo'); // baz = 123 +def = require('Settings').get('test', 'jkl', 789); // def = 789 +all = require('Settings').get('test'); // all = {foo: 123, bar: 456} +baz = require('Settings').get('test', 'baz'); // baz = undefined + +// read global setting +vibrate = require('Settings').get(Bangle, 'vibrate', true); + +// Hint: if your app reads multiple settings, you can create a helper function: +function s(key, def) { return require('Settings').get('myapp', key, def); } +var foo = s('foo setting', 'default value'), bar = s('bar setting'); +``` + +*/ + +/** + * Read setting value from file + * + * @param {string} file Settings file + * @param {string} key Setting to get, omit to get all settings as object + * @param {*} def Default value + * @return {*} Setting value (or default if not found) + */ +function get(file, key, def) { + var s = require("Storage").readJSON(file); + if (def===undefined && ["object", "undefined"].includes(typeof key)) { + // get(file) or get(file, def): get all settings + return (s!==undefined) ? s : key; + } + return ((typeof s==="object") && (key in s)) ? s[key] : def; +} + +/** + * Write setting value to file + * + * @param {string} file Settings file + * @param {string} key Setting to change, omit to replace all settings + * @param {*} value Value to store + */ +function set(file, key, value) { + if (value===undefined && typeof key==="object") { + // set(file, value): overwrite settings completely + require("Storage").writeJSON(file, key); + return; + } + var s = require("Storage").readJSON(file, 1); + if (typeof s!=="object") s = {}; + s[key] = value; + require("Storage").write(file, s); +} + +/** + * Read setting value + * + * @param {string|object} app App name or Bangle + * @param {string} key Setting to get, omit to get all settings as object + * @param {*} def Default value + * @return {*} Setting value (or default if not found) + */ +exports.get = function(app, key, def) { + return get((app===Bangle) ? 'setting.json' : app+".json", key, def); +}; + +/** + * Write setting value + * + * @param {string|object} app App name or Bangle + * @param {string} key Setting to change, omit to replace all settings + * @param {*} val Value to store + */ +exports.set = function(app, key, val) { + set((app===Bangle) ? 'setting.json' : app+".json", key, val); +};