From d644cba38ff06369cc43c1ebd08d344fd1d248ea Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Mon, 31 Aug 2015 15:09:53 +0200 Subject: Replace the component bounded `cachedValue` by a global `UnsavedEdits` This new draft saving system is currently only implemented for the card description and comment. We need better a component inheritance/composition model to support this for all editable fields. Fixes #186 --- client/lib/unsavedEdits.js | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 client/lib/unsavedEdits.js (limited to 'client/lib/unsavedEdits.js') diff --git a/client/lib/unsavedEdits.js b/client/lib/unsavedEdits.js new file mode 100644 index 00000000..55ea2529 --- /dev/null +++ b/client/lib/unsavedEdits.js @@ -0,0 +1,82 @@ +Meteor.subscribe('unsaved-edits'); + +// `UnsavedEdits` is a global key-value store used to save drafts of user +// inputs. We used to have the notion of a `cachedValue` that was local to a +// component but the global store has multiple advantages: +// 1. When the component is unmounted (ie, destroyed) the draft isn't lost +// 2. The drafts are synced across multiple computers +// 3. The drafts are synced across multiple browser tabs +// XXX This currently doesn't work in purely offline mode since the sync is +// handled with the DDP connection to the server. To solve this, we could use +// something like GroundDB that syncs using localstorage. +// +// The key is a dictionary composed of two fields: +// * a `fieldName` which identifies the particular field. Since this is a global +// identifier a good practice would be to compose it with the collection name +// and the document field, eg. `boardTitle`, `cardDescription`. +// * a `docId` which identifies the appropriate document. In general we use +// MongoDB `_id` field. +// +// The value is a string containing the draft. + +UnsavedEdits = { + // XXX Wanted to have the collection has an instance variable, but + // unfortunately the collection isn't defined yet at this point. We need ES6 + // modules to solve the file order issue! + // + // _collection: UnsavedEditCollection, + + get({ fieldName, docId }, defaultTo = '') { + let unsavedValue = this._getCollectionDocument(fieldName, docId); + if (unsavedValue) { + return unsavedValue.value + } else { + return defaultTo; + } + }, + + has({ fieldName, docId }) { + return Boolean(this.get({fieldName, docId})); + }, + + set({ fieldName, docId }, value) { + let currentDoc = this._getCollectionDocument(fieldName, docId); + if (currentDoc) { + UnsavedEditCollection.update(currentDoc._id, { + $set: { + value: value + } + }); + } else { + UnsavedEditCollection.insert({ + fieldName, + docId, + value, + }); + } + }, + + reset({ fieldName, docId }) { + let currentDoc = this._getCollectionDocument(fieldName, docId); + if (currentDoc) { + UnsavedEditCollection.remove(currentDoc._id); + } + }, + + _getCollectionDocument(fieldName, docId) { + return UnsavedEditCollection.findOne({fieldName, docId}); + } +} + +Blaze.registerHelper('getUnsavedValue', (fieldName, docId, defaultTo) => { + // Workaround some blaze feature that ass a list of keywords arguments as the + // last parameter (even if the caller didn't specify any). + if (! _.isString(defaultTo)) { + defaultTo = ''; + } + return UnsavedEdits.get({ fieldName, docId }, defaultTo); +}); + +Blaze.registerHelper('hasUnsavedValue', (fieldName, docId) => { + return UnsavedEdits.has({ fieldName, docId }); +}); -- cgit v1.2.3-1-g7c22