unsavedEdits.js 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. // `UnsavedEdits` is a global key-value store used to save drafts of user
  2. // inputs. We used to have the notion of a `cachedValue` that was local to a
  3. // component but the global store has multiple advantages:
  4. // 1. When the component is unmounted (ie, destroyed) the draft isn't lost
  5. // 2. The drafts are synced across multiple computers
  6. // 3. The drafts are synced across multiple browser tabs
  7. // XXX This currently doesn't work in purely offline mode since the sync is
  8. // handled with the DDP connection to the server. To solve this, we could use
  9. // something like GroundDB that syncs using localstorage.
  10. //
  11. // The key is a dictionary composed of two fields:
  12. // * a `fieldName` which identifies the particular field. Since this is a global
  13. // identifier a good practice would be to compose it with the collection name
  14. // and the document field, eg. `boardTitle`, `cardDescription`.
  15. // * a `docId` which identifies the appropriate document. In general we use
  16. // MongoDB `_id` field.
  17. //
  18. // The value is a string containing the draft.
  19. UnsavedEdits = {
  20. // XXX Wanted to have the collection has an instance variable, but
  21. // unfortunately the collection isn't defined yet at this point. We need ES6
  22. // modules to solve the file order issue!
  23. //
  24. // _collection: UnsavedEditCollection,
  25. get({ fieldName, docId }, defaultTo = '') {
  26. const unsavedValue = this._getCollectionDocument(fieldName, docId);
  27. if (unsavedValue) {
  28. return unsavedValue.value;
  29. } else {
  30. return defaultTo;
  31. }
  32. },
  33. has({ fieldName, docId }) {
  34. return Boolean(this.get({ fieldName, docId }));
  35. },
  36. set({ fieldName, docId }, value) {
  37. const currentDoc = this._getCollectionDocument(fieldName, docId);
  38. if (currentDoc) {
  39. UnsavedEditCollection.update(currentDoc._id, { $set: { value } });
  40. } else {
  41. UnsavedEditCollection.insert({
  42. fieldName,
  43. docId,
  44. value,
  45. });
  46. }
  47. },
  48. reset({ fieldName, docId }) {
  49. const currentDoc = this._getCollectionDocument(fieldName, docId);
  50. if (currentDoc) {
  51. UnsavedEditCollection.remove(currentDoc._id);
  52. }
  53. },
  54. _getCollectionDocument(fieldName, docId) {
  55. return UnsavedEditCollection.findOne({ fieldName, docId });
  56. },
  57. };
  58. Blaze.registerHelper('getUnsavedValue', (fieldName, docId, defaultTo) => {
  59. // Workaround some blaze feature that pass a list of keywords arguments as the
  60. // last parameter (even if the caller didn't specify any).
  61. if (!_.isString(defaultTo)) {
  62. defaultTo = '';
  63. }
  64. return UnsavedEdits.get({ fieldName, docId }, defaultTo);
  65. });
  66. Blaze.registerHelper('hasUnsavedValue', (fieldName, docId) => {
  67. return UnsavedEdits.has({ fieldName, docId });
  68. });