diff --git a/ipc.js b/app/ipc.js similarity index 100% rename from ipc.js rename to app/ipc.js diff --git a/app/sqlite3db.js b/app/sqlite3db.js new file mode 100644 index 0000000..fc3e323 --- /dev/null +++ b/app/sqlite3db.js @@ -0,0 +1,107 @@ +const { app, ipcMain } = require('electron'); +const path = require('path'); +const sqlite3 = require('sqlite3').verbose(); +const mkdirp = require('mkdirp'); +const touch = require('touch'); + +module.exports = (function(){ + + const upgrade_map = { + 0: upgrade_0_to_1 + }; + + var db; + + function open_db() { + const default_db_root = path.join(app.getPath('userData')); + const default_db = path.join(default_db_root, 'default.db'); + + mkdirp(default_db_root, function(err){ + if(err) { + console.error(err); + } else { + touch(default_db, function() { + db = new sqlite3.Database(default_db, sqlite3.OPEN_READWRITE, (err) => { + if(err){ + console.error('Failed to open database at ' + default_db); + console.error(err); + } else { + console.log('Connected to database'); + load_version(); + } + }); + }); + } + }); + } + + function load_version() { + db.get('PRAGMA user_version;', (err, result) => { + if(err) { + console.error(err); + } + console.log('Database version ' + result.user_version); + if(result.user_version <= Math.max(Object.keys(upgrade_map))) { + console.log('Upgrading database...'); + upgrade_map[result.user_version](); + } + }); + } + + function upgrade_0_to_1() { + var query = [ + 'CREATE TABLE IF NOT EXISTS corkboard (', + 'id INTEGER PRIMARY KEY,', + 'type TEXT NOT NULL,', + 'content TEXT,', + 'title TEXT,', + 'x INTEGER,', + 'y INTEGER,', + 'z INTEGER,', + 'deleted INTEGER DEFAULT 0,', + 'settings TEXT', + ');' + ].join(''); + + db.get(query, (err) => { + if(err) { + console.error(err); + } + complete_upgrade(1); + }); + } + + function complete_upgrade(version) { + var query = 'PRAGMA user_version=' + version + ';'; + db.get(query, (err) => { + if(err) { + console.error(err); + } + load_version(); + }); + } + + ipcMain.on('sqlite3_db_all', (event, arg) => { + console.log('Received db transaction request ' + arg.id); + db.all(arg.query, arg.params, (err, rows) => { + if(err) { + console.error(err); + } + console.log('Completed db transaction request ' + arg.id); + event.sender.send('sqlite3_db_all_callback', { + id: arg.id, + err: err, + rows: rows + }); + }); + }); + + return open_db; + +})(); + + + + + + diff --git a/window.js b/app/window.js similarity index 90% rename from window.js rename to app/window.js index 8e35953..81caf5f 100644 --- a/window.js +++ b/app/window.js @@ -19,7 +19,7 @@ module.exports = function(index) { win.setMenu(null); win.loadURL(url.format({ - pathname: path.join(__dirname, index), + pathname: path.resolve(__dirname, '..', index), protocol: 'file:', slashes: true })); diff --git a/data/indexeddb.js b/data/indexeddb.js deleted file mode 100644 index b49f916..0000000 --- a/data/indexeddb.js +++ /dev/null @@ -1,22 +0,0 @@ -import { A_LOAD_ALL } from '../state/action_types'; - -export const DB_STICKIES = 'stickies'; -export const DB_POLAROIDS = 'polaroids'; -export const DB_CORKBOARD = 'corkboard'; -export var db; - -var db_open = indexedDB.open('corkboard', 3); - -db_open.onupgradeneeded = function() { - console.log('IndexedDB: Upgraded database'); - db = db_open.result; - - if(!db.objectStoreNames.contains(DB_CORKBOARD)) { - db.createObjectStore(DB_CORKBOARD, { keyPath: 'id' }); - } -}; - -db_open.onsuccess = function() { - db = db_open.result; - window.vm.$store.dispatch(A_LOAD_ALL); -}; \ No newline at end of file diff --git a/index.js b/index.js index a25e197..b613be2 100644 --- a/index.js +++ b/index.js @@ -4,19 +4,27 @@ import store from './state'; import { clipboard } from 'electron'; import { A_APP_TOGGLE_GODMODE, - A_BOARD_ADD_ITEM + A_BOARD_ADD_ITEM, + A_LOAD_ALL } from './state/action_types'; +import rem_to_px from './utils/rem_to_px'; + window.vm = new Vue({ el: '#corkboard', store, created: function() { document.onkeydown = this.keydown; }, + mounted: function() { + this.$store.dispatch(A_LOAD_ALL); + }, methods: { keydown: function(e) { if(e.key == 'v' && e.ctrlKey) { this.paste(); + } else if (e.key == 'n' && e.ctrlKey) { + this.new_sticky(); } else if (e.key == 'r' && e.ctrlKey) { location.reload(); } else if (e.key == 's' && e.ctrlKey) { @@ -25,14 +33,22 @@ window.vm = new Vue({ this.$store.dispatch(A_APP_TOGGLE_GODMODE); } }, + new_sticky: function() { + this.$store.dispatch(A_BOARD_ADD_ITEM, { + x: (window.innerWidth - rem_to_px(15)) / 2, + y: (window.innerHeight - rem_to_px(15)) / 2, + z: this.$store.getters.item_max_field('z') + 1, + type: 'sticky' + }); + }, paste: function() { var clipboard_text = clipboard.readText(); if(clipboard_text) { console.log('Pasting text ' + clipboard_text + '...'); this.$store.dispatch(A_BOARD_ADD_ITEM, { - x: window.innerWidth / 2, - y: window.innerHeight / 2, + x: (window.innerWidth - rem_to_px(15)) / 2, + y: (window.innerHeight - rem_to_px(15)) / 2, z: this.$store.getters.item_max_field('z') + 1, type: 'sticky', content: clipboard_text.trim() diff --git a/main.js b/main.js index d8e6591..63f338f 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,7 @@ const { app } = require('electron'); -const window = require('./window'); -const ipc_setup = require('./ipc'); +const window = require('./app/window'); +const ipc_setup = require('./app/ipc'); +const database_setup = require('./app/sqlite3db'); let win; @@ -11,6 +12,7 @@ app.on('ready', function() { win = null; }); ipc_setup(win); + database_setup(); console.log('Application started'); }); diff --git a/package-lock.json b/package-lock.json index 14e74bf..49fde30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "corkboard", - "version": "1.4.0", + "version": "1.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -66,6 +66,11 @@ "integrity": "sha512-X/pIUOcgpX7xxKsmdPCMKeDBefsGH/4D/IuJ1gIHbqgWI0qEy/yMKeqaN/sT+rzV9UpAXAfd0kLOVExRmZrXIg==", "dev": true }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "acorn": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", @@ -6807,8 +6812,7 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mississippi": { "version": "2.0.0", @@ -6901,7 +6905,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } @@ -6944,6 +6947,11 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "nan": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz", + "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==" + }, "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", @@ -7097,6 +7105,14 @@ } } }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1.1.1" + } + }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -9542,6 +9558,411 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sqlite3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.0.tgz", + "integrity": "sha512-6OlcAQNGaRSBLK1CuaRbKwlMFBb9DEhzmZyQP+fltNRF6XcIMpVIfXCBEcXPe1d4v9LnhkQUYkknDbA5JReqJg==", + "requires": { + "nan": "2.9.2", + "node-pre-gyp": "0.9.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.5" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "iconv-lite": { + "version": "0.4.19", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.2.1", + "bundled": true, + "requires": { + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.19", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.9.0", + "bundled": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.6", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "rc": { + "version": "1.2.6", + "bundled": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "readable-stream": { + "version": "2.3.5", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true + }, + "sax": { + "version": "1.2.4", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "tar": { + "version": "4.4.0", + "bundled": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.1", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, "sshpk": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", @@ -10144,6 +10565,14 @@ "repeat-string": "1.6.1" } }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "requires": { + "nopt": "1.0.10" + } + }, "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", diff --git a/package.json b/package.json index 2489915..1b79d6b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "corkboard", "productName": "Corkboard", - "version": "1.4.0", + "version": "1.5.0", "description": "Sticky Notes & Pictures", "main": "main.js", "scripts": { @@ -13,7 +13,8 @@ "pack": "cross-env NODE_ENV=production electron-builder --dir", "dist": "cross-env NODE_ENV=production electron-builder", "lint": "cross-env eslint ./ --ext .vue", - "lint-fix": "cross-env eslint ./ --fix --ext .vue" + "lint-fix": "cross-env eslint ./ --fix --ext .vue", + "postinstall": "cross-env electron-builder install-app-deps" }, "author": "R. J. Young", "license": "MIT", @@ -44,6 +45,9 @@ "@fortawesome/fontawesome-free-regular": "^5.0.9", "@fortawesome/fontawesome-free-solid": "^5.0.9", "@fortawesome/vue-fontawesome": "0.0.22", + "mkdirp": "^0.5.1", + "sqlite3": "^4.0.0", + "touch": "^3.1.0", "vue": "^2.5.16", "vuex": "^3.0.1" }, diff --git a/state/board/actions.js b/state/board/actions.js index fe72033..ebadb75 100644 --- a/state/board/actions.js +++ b/state/board/actions.js @@ -1,3 +1,4 @@ +import { logIf, errorIf } from '../../utils/logwrap'; import { A_LOAD_ALL, A_BOARD_ADD_ITEM, @@ -15,71 +16,132 @@ import { M_BOARD_MOVE_ACTION_GROUP, M_BOARD_PROMOTE_ACTION_GROUP, M_BOARD_ITEM_SET_FIELD, - M_BOARD_ITEM_SAVE, M_BOARD_ITEM_DELETE } from '../mutation_types'; -import { db, DB_CORKBOARD } from '../../data/indexeddb'; +import { db, combine_settings, extract_settings } from '../sqlite3db_proxy'; + +const DEBUG = false; export default { [A_LOAD_ALL]: function(context) { - var tx = db.transaction(DB_CORKBOARD, 'readwrite'); - var store = tx.objectStore(DB_CORKBOARD); - var req = store.getAll(); - - req.onsuccess = function() { - for(var i = 0; i < req.result.length; i++) { - context.commit(M_BOARD_ADD_ITEM, req.result[i]); - } - }; + var query = 'SELECT id, type, content, title, x, y, z, settings from corkboard;'; + db.all(query, (err, rows) => { + errorIf(err, err); + combine_settings(rows); + rows.forEach((row) => { context.commit(M_BOARD_ADD_ITEM, row); }); + }); }, [A_BOARD_ADD_ITEM]: function(context, item) { - item.id = item.id || (new Date().getTime()); - var tx = db.transaction(DB_CORKBOARD, 'readwrite'); - var store = tx.objectStore(DB_CORKBOARD); - var req = store.put(item); + var query = [ + 'INSERT INTO corkboard (', + 'type, content, title, x, y, z, settings', + ') VALUES (', + '?, ?, ?, ?, ?, ?, ?', + ');' + ].join(''); + + var params = [ + item.type, + item.content, + item.title, + item.x, + item.y, + item.z, + extract_settings(item) + ]; - req.onsuccess = function() { - context.commit(M_BOARD_ADD_ITEM, item); - }; + db.all(query, params, (err) => { + errorIf(err, err); + db.all('SELECT last_insert_rowid();', (err, rows) => { + errorIf(err, err); + item.id = rows[0]['last_insert_rowid()']; + logIf(DEBUG, 'Added item ' + item.id); + context.commit(M_BOARD_ADD_ITEM, item); + }); + }); }, [A_BOARD_MOVE_START]: function(context, index) { + logIf(DEBUG, 'Starting move action...'); context.commit(M_BOARD_START_ACTION, index); + logIf(DEBUG, 'Bringing action group to top'); context.commit(M_BOARD_PROMOTE_ACTION_GROUP); + logIf(DEBUG, 'Move started'); }, [A_BOARD_MOVE]: function(context, payload) { context.commit(M_BOARD_MOVE_ACTION_GROUP, payload); }, [A_BOARD_MOVE_FINISH]: function(context) { - var tx = db.transaction(DB_CORKBOARD, 'readwrite'); - var store = tx.objectStore(DB_CORKBOARD); + logIf(DEBUG, 'Finishing move action...'); context.getters.board_action_group.forEach((item) => { - var req = store.put(item); - req.onsuccess = function() { - console.log('Saved item ' + item.id); - }; + var query = [ + 'UPDATE corkboard SET', + 'type=?, content=?, title=?, x=?, y=?, z=?, settings=?', + 'WHERE id=?' + ].join(' '); + + var params = [ + item.type, + item.content, + item.title, + item.x, + item.y, + item.z, + extract_settings(item), + item.id + ]; + + db.all(query, params, (err) => { + errorIf(err, err); + logIf(DEBUG, 'Saved item ' + item.id); + }); }); context.commit(M_BOARD_FINISH_ACTION); + logIf(DEBUG, 'Move finished'); }, [A_BOARD_ITEM_SET_FIELD]: function(context, payload) { context.commit(M_BOARD_ITEM_SET_FIELD, payload); - if(!(payload.hasOwnProperty('save') && payload.save == false)) { - context.commit(M_BOARD_ITEM_SAVE, payload.index); - } + var item = context.getters.board_item_by_index(payload.index); + + var query = [ + 'UPDATE corkboard SET', + 'type=?, content=?, title=?, x=?, y=?, z=?, settings=?', + 'WHERE id=?' + ].join(' '); + + var params = [ + item.type, + item.content, + item.title, + item.x, + item.y, + item.z, + extract_settings(item), + item.id + ]; + + db.all(query, params, (err) => { + errorIf(err, err); + logIf(DEBUG, 'Saved item ' + item.id); + }); + }, [A_BOARD_ITEM_DELETE]: function(context, index) { var id = context.getters.board_item_by_index(index).id; - var tx = db.transaction(DB_CORKBOARD, 'readwrite'); - var store = tx.objectStore(DB_CORKBOARD); - var req = store.delete(id); - req.onsuccess = function() { + var query = [ + 'DELETE FROM corkboard', + 'WHERE id=?;' + ].join(' '); + + var params = [ + id + ]; + + db.all(query, params, (err) => { + errorIf(err, err); + logIf(DEBUG, 'Deleted item ' + id); context.commit(M_BOARD_ITEM_DELETE, index); - console.log('Sticky ' + id + ' deleted'); - }; - req.onerror = function(event) { - console.error('Error deleting Sticky ' + id); - console.error(event); - }; + }); } }; \ No newline at end of file diff --git a/state/board/mutations.js b/state/board/mutations.js index 4cd30f5..acec094 100644 --- a/state/board/mutations.js +++ b/state/board/mutations.js @@ -1,5 +1,5 @@ import Vue from 'vue'; - +import { logIf } from '../../utils/logwrap'; import { M_BOARD_ADD_ITEM, M_BOARD_ITEM_SET_FIELD, @@ -7,18 +7,16 @@ import { M_BOARD_FINISH_ACTION, M_BOARD_MOVE_ACTION_GROUP, M_BOARD_PROMOTE_ACTION_GROUP, - M_BOARD_ITEM_SAVE, M_BOARD_ITEM_DELETE } from '../mutation_types'; -import { db, DB_CORKBOARD } from '../../data/indexeddb'; +const DEBUG = true; export default { [M_BOARD_ADD_ITEM]: function(state, item) { state.items.push(item); }, [M_BOARD_ITEM_SET_FIELD]: function(state, payload) { - // console.log('M_BOARD_ITEM_SET_FIELD: payload=' + JSON.stringify(payload)); Vue.set(state.items[payload.index], payload.field, payload.value); }, [M_BOARD_START_ACTION]: function(state, index) { @@ -43,20 +41,20 @@ export default { // Child on top of parent if(current.z <= parent.z) { - console.log(parent.id + ' -/-> ' + current.id); + logIf(DEBUG, parent.id + ' -/-> ' + current.id + ' (z below)'); continue; } // Child below parent if(current.y - parent.y > parent.height) { - console.log(parent.id + ' -/-> ' + current.id); + logIf(DEBUG, parent.id + ' -/-> ' + current.id + ' (y below)'); continue; } // Parent left of child if(parent.x < current.x) { if((current.x - parent.x) > parent.width) { - console.log(parent.id + ' -/-> ' + current.id); + logIf(DEBUG, parent.id + ' -/-> ' + current.id + ' (x right)'); continue; } } @@ -64,12 +62,12 @@ export default { // Child left of parent if(parent.x >= current.x) { if((parent.x - current.x) > current.width) { - console.log(parent.id + ' -/-> ' + current.id); + logIf(DEBUG, parent.id + ' -/-> ' + current.id + ' (x left)'); continue; } } - console.log(parent.id + ' ---> ' + current.id); + logIf(DEBUG, parent.id + ' ---> ' + current.id); state.action_group.push(current); break; } @@ -99,14 +97,6 @@ export default { [M_BOARD_FINISH_ACTION]: function(state) { state.action_group = []; }, - [M_BOARD_ITEM_SAVE]: function(state, index) { - var tx = db.transaction(DB_CORKBOARD, 'readwrite'); - var store = tx.objectStore(DB_CORKBOARD); - var req = store.put(state.items[index]); - req.onsuccess = function() { - console.log('Saved item ' + state.items[index].id); - }; - }, [M_BOARD_ITEM_DELETE]: function(state, index) { state.items.splice(index, 1); } diff --git a/state/sqlite3db_proxy.js b/state/sqlite3db_proxy.js new file mode 100644 index 0000000..88c952f --- /dev/null +++ b/state/sqlite3db_proxy.js @@ -0,0 +1,55 @@ +import { ipcRenderer } from 'electron'; +import { logIf } from '../utils/logwrap'; + +const DEBUG = false; + +const callbacks = {}; +let transaction_id = 0; + +ipcRenderer.on('sqlite3_db_all_callback', (event, arg) => { + logIf(DEBUG, 'Received db transaction request ' + arg.id); + if(callbacks[arg.id]) { + callbacks[arg.id](arg.err, arg.rows); + delete callbacks[arg.id]; + } +}); + +export const extract_settings = function(item) { + var settings = Object.assign({}, item); + ['id', 'type', 'content', 'title', 'x', 'y', 'z'].forEach((val) => { + delete settings[val]; + }); + return JSON.stringify(settings); +}; + +export const combine_settings = function(item) { + if(Array.isArray(item)) { + item.forEach((item) => { combine_settings(item); }); + } else { + var settings = JSON.parse(item.settings); + Object.assign(item, settings); + delete item.settings; + } +}; + +export const db = { + all: function(query, params, callback) { + var parcel = { + id: transaction_id++, + query: query + }; + + switch(arguments.length) { + case 2: + callbacks[parcel.id] = params; + break; + case 3: + parcel.params = params; + callbacks[parcel.id] = callback; + break; + } + + ipcRenderer.send('sqlite3_db_all', parcel); + logIf(DEBUG, 'Sent db transaction request ' + parcel.id); + } +}; \ No newline at end of file diff --git a/utils/logwrap.js b/utils/logwrap.js new file mode 100644 index 0000000..14d86b2 --- /dev/null +++ b/utils/logwrap.js @@ -0,0 +1,21 @@ +export const log = console.log; +export const warn = console.warn; +export const error = console.error; + +export const logIf = function() { + if(arguments[0]) { + log.apply(null, Array.prototype.slice.call(arguments, 1)); + } +}; + +export const warnIf = function() { + if(arguments[0]) { + warn.apply(null, Array.prototype.slice.call(arguments, 1)); + } +}; + +export const errorIf = function() { + if(arguments[0]) { + error.apply(null, Array.prototype.slice.call(arguments, 1)); + } +}; \ No newline at end of file diff --git a/view/corkboard_item.vue b/view/corkboard_item.vue index 4638b97..eb7c79e 100644 --- a/view/corkboard_item.vue +++ b/view/corkboard_item.vue @@ -8,15 +8,18 @@ }" class="corkboard_item" @dblclick="doubleclick" - @mousedown="mousedown"> + @mousedown="mousedown" + @click="click"> @@ -70,6 +73,9 @@ export default { value: this.$store.getters.item_max_field('z') + 1 }); }, + click: function() { + this.$refs.child.focus(); + }, resize: function() { this.$store.dispatch(A_BOARD_ITEM_SET_FIELD, { index: this.index, diff --git a/view/icon_wrapper.vue b/view/icon_wrapper.vue index e1272e5..476a746 100644 --- a/view/icon_wrapper.vue +++ b/view/icon_wrapper.vue @@ -22,6 +22,7 @@ import faSearch from '@fortawesome/fontawesome-free-solid/faSearch'; import faSearchPlus from '@fortawesome/fontawesome-free-solid/faSearchPlus'; import faSearchMinus from '@fortawesome/fontawesome-free-solid/faSearchMinus'; import faClone from '@fortawesome/fontawesome-free-solid/faClone'; +import faTextHeight from '@fortawesome/fontawesome-free-solid/faTextHeight'; var icon_map = { trash: faTrash, @@ -36,7 +37,8 @@ var icon_map = { search_plus: faSearchPlus, search_minus: faSearchMinus, search: faSearch, - clone: faClone + clone: faClone, + text_height: faTextHeight }; export default { diff --git a/view/photo_toolbar.vue b/view/photo_toolbar.vue index f1f4e97..cdf2986 100644 --- a/view/photo_toolbar.vue +++ b/view/photo_toolbar.vue @@ -39,8 +39,16 @@ export default { .toolbar { position: fixed; - right: 0.2rem; - top: 0.2rem; + box-sizing: border-box; + right: 0rem; + top: -2rem; + padding: 0.4rem; + user-select: none; + height: 2rem; +} + +.toolbar:hover { + background: rgba(0, 0, 0, 0.2); } .toolbar svg { @@ -50,11 +58,11 @@ export default { } .toolbar:hover svg { - color: rgba(0, 0, 0, 0.2); + color: rgba(255, 255, 255, 0.5); } .toolbar svg:hover { - color: rgba(0, 0, 0, 0.3); + color: rgba(255, 255, 255, 0.8); } \ No newline at end of file diff --git a/view/sticky.vue b/view/sticky.vue index b0e63f6..6b75087 100644 --- a/view/sticky.vue +++ b/view/sticky.vue @@ -1,15 +1,24 @@ + + + + + @click="clone_click" /> + + @click="toggle_click($event, 'bold')" /> + + @click="size_click" /> + @click="trash_click" /> @@ -29,11 +43,10 @@ import icon_wrapper from './icon_wrapper.vue'; import { A_BOARD_ITEM_SET_FIELD, - A_BOARD_ITEM_DELETE + A_BOARD_ITEM_DELETE, + A_BOARD_ADD_ITEM } from '../state/action_types'; -var colours = ['#ffff88', '#88ff88', '#88ffff', '#ff88ff']; - export default { name: 'StickyToolbar', components: { @@ -53,6 +66,20 @@ export default { trash_click: function() { this.$store.dispatch(A_BOARD_ITEM_DELETE, this.index); }, + clone_click: function() { + this.$store.dispatch(A_BOARD_ADD_ITEM, { + type: 'sticky', + x: this.sticky.x, + y: this.sticky.y + this.sticky.height, + z: this.$store.getters.item_max_field('z') + 1, + content: this.sticky.content, + bold: this.sticky.bold, + centre: this.sticky.centre, + colour: this.sticky.colour, + size: this.sticky.size, + dense: this.sticky.dense + }); + }, toggle_click: function(e, field) { var current = this.sticky[field] || false; this.$store.dispatch(A_BOARD_ITEM_SET_FIELD, { @@ -61,12 +88,20 @@ export default { value: !current }); }, + size_click: function() { + var current = +this.sticky.size || 0; + this.$store.dispatch(A_BOARD_ITEM_SET_FIELD, { + index: this.index, + field: 'size', + value: (current + 1) % 3 + }); + }, colour_click: function() { - var current = this.sticky.colour || colours[0]; + var current = +this.sticky.colour || 0; this.$store.dispatch(A_BOARD_ITEM_SET_FIELD, { index: this.index, field: 'colour', - value: colours[(colours.indexOf(current) + 1) % colours.length] + value: current + 1 % 3 }); } } @@ -76,35 +111,40 @@ export default { \ No newline at end of file