summaryrefslogtreecommitdiffstats
path: root/client/lib/unsavedEdits.js
blob: 17bb29b5ac251338c6ac0bd9d6a48264420d08d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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 = '') {
    const 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) {
    const currentDoc = this._getCollectionDocument(fieldName, docId);
    if (currentDoc) {
      UnsavedEditCollection.update(currentDoc._id, { $set: { value }});
    } else {
      UnsavedEditCollection.insert({
        fieldName,
        docId,
        value,
      });
    }
  },

  reset({ fieldName, docId }) {
    const 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 pass 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 });
});