Cache documents and collection

This commit is contained in:
Jori Lallo 2017-07-16 11:47:48 -07:00
parent 3b2ad193d5
commit 9b808efd7a
9 changed files with 207 additions and 15 deletions

View File

@ -13,6 +13,7 @@ import Flex from 'components/Flex';
import stores from 'stores';
import DocumentsStore from 'stores/DocumentsStore';
import CollectionsStore from 'stores/CollectionsStore';
import CacheStore from 'stores/CacheStore';
import 'normalize.css/normalize.css';
import 'styles/base.scss';
@ -58,11 +59,15 @@ const Auth = ({ children }: AuthProps) => {
if (!authenticatedStores) {
// Stores for authenticated user
const user = stores.auth.getUserStore();
const cache = new CacheStore(user.user.id);
authenticatedStores = {
user,
documents: new DocumentsStore(),
documents: new DocumentsStore({
cache,
}),
collections: new CollectionsStore({
teamId: user.team.id,
cache,
}),
};

View File

@ -12,6 +12,7 @@ class Collection {
isSaving: boolean = false;
hasPendingChanges: boolean = false;
errors: ErrorsStore;
data: Object;
createdAt: string;
description: ?string;
@ -64,10 +65,8 @@ class Collection {
});
}
invariant(res && res.data, 'Data should be available');
this.updateData({
...res.data,
hasPendingChanges: false,
});
this.updateData(res.data);
this.hasPendingChanges = false;
} catch (e) {
this.errors.add('Collection failed saving');
return false;
@ -79,6 +78,7 @@ class Collection {
};
updateData(data: Object = {}) {
this.data = data;
extendObservable(this, data);
}

View File

@ -39,6 +39,8 @@ class Document {
url: string;
views: number;
data: Object;
/* Computed */
@computed get modifiedSinceViewed(): boolean {
@ -148,8 +150,8 @@ class Document {
invariant(res && res.data, 'Data should be available');
this.updateData({
...res.data,
hasPendingChanges: false,
});
this.hasPendingChanges = false;
} catch (e) {
this.errors.add('Document failed saving');
} finally {
@ -161,7 +163,8 @@ class Document {
updateData(data: Object = {}, dirty: boolean = false) {
if (data.text) data.title = parseHeader(data.text);
if (dirty) data.hasPendingChanges = true;
if (dirty) this.hasPendingChanges = true;
this.data = data;
extendObservable(this, data);
}

View File

@ -0,0 +1,28 @@
// @flow
import localForage from 'localforage';
class CacheStore {
key: string;
cacheKey = (key: string): string => {
return `CACHE_${this.key}_${key}`;
};
getItem = (key: string): any => {
return localForage.getItem(this.cacheKey(key));
};
setItem = (key: string, value: any): any => {
return localForage.setItem(this.cacheKey(key), value);
};
removeItem = (key: string) => {
return localForage.removeItem(this.cacheKey(key));
};
constructor(cacheKey: string) {
this.key = cacheKey;
}
}
export default CacheStore;

View File

@ -1,5 +1,11 @@
// @flow
import { observable, action, runInAction, ObservableArray } from 'mobx';
import {
observable,
action,
runInAction,
ObservableArray,
autorunAsync,
} from 'mobx';
import ApiClient, { client } from 'utils/ApiClient';
import _ from 'lodash';
import invariant from 'invariant';
@ -7,9 +13,13 @@ import invariant from 'invariant';
import stores from 'stores';
import Collection from 'models/Collection';
import ErrorsStore from 'stores/ErrorsStore';
import CacheStore from 'stores/CacheStore';
const COLLECTION_CACHE_KEY = 'COLLECTION_CACHE_KEY';
type Options = {
teamId: string,
cache: CacheStore,
};
class CollectionsStore {
@ -19,6 +29,7 @@ class CollectionsStore {
client: ApiClient;
teamId: string;
errors: ErrorsStore;
cache: CacheStore;
/* Actions */
@ -54,6 +65,22 @@ class CollectionsStore {
this.client = client;
this.errors = stores.errors;
this.teamId = options.teamId;
this.cache = options.cache;
this.cache.getItem(COLLECTION_CACHE_KEY).then(data => {
if (data) {
this.data.replace(data.map(collection => new Collection(collection)));
this.isLoaded = true;
}
});
autorunAsync('CollectionsStore.persists', () => {
if (this.data.length > 0)
this.cache.setItem(
COLLECTION_CACHE_KEY,
this.data.map(collection => collection.data)
);
});
}
}

View File

@ -1,5 +1,12 @@
// @flow
import { observable, action, computed, ObservableMap, runInAction } from 'mobx';
import {
observable,
action,
computed,
ObservableMap,
runInAction,
autorunAsync,
} from 'mobx';
import { client } from 'utils/ApiClient';
import _ from 'lodash';
import invariant from 'invariant';
@ -7,12 +14,17 @@ import invariant from 'invariant';
import stores from 'stores';
import Document from 'models/Document';
import ErrorsStore from 'stores/ErrorsStore';
import CacheStore from 'stores/CacheStore';
const DOCUMENTS_CACHE_KEY = 'DOCUMENTS_CACHE_KEY';
class DocumentsStore {
@observable recentlyViewedIds: Array<string> = [];
@observable data: Map<string, Document> = new ObservableMap([]);
@observable isLoaded: boolean = false;
errors: ErrorsStore;
cache: CacheStore;
/* Computed */
@ -96,8 +108,24 @@ class DocumentsStore {
return _.find(this.data.values(), { url });
};
constructor() {
constructor(options: Options) {
this.errors = stores.errors;
this.cache = options.cache;
this.cache.getItem(DOCUMENTS_CACHE_KEY).then(data => {
if (data) {
data.forEach(document => this.add(new Document(document)));
}
});
autorunAsync('DocumentsStore.persists', () => {
if (this.data.size) {
this.cache.setItem(
DOCUMENTS_CACHE_KEY,
Array.from(this.data.values()).map(collection => collection.data)
);
}
});
}
}

View File

@ -118,6 +118,7 @@
"koa-mount": "^3.0.0",
"koa-router": "7.0.1",
"koa-sendfile": "2.0.0",
"localforage": "^1.5.0",
"lodash": "^4.17.4",
"lodash.orderby": "4.4.0",
"marked": "0.3.6",

View File

@ -37,6 +37,8 @@ module.exports = {
},
{ test: /\.md/, loader: 'raw-loader' },
],
// Silence warning https://github.com/localForage/localForage/issues/599
noParse: [new RegExp('node_modules/localforage/dist/localforage.js')],
},
resolve: {
root: path.join(__dirname, 'frontend'),

108
yarn.lock
View File

@ -44,6 +44,10 @@ acorn-jsx@^3.0.0, acorn-jsx@^3.0.1:
dependencies:
acorn "^3.0.4"
acorn@^1.0.3:
version "1.2.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-1.2.2.tgz#c8ce27de0acc76d896d2b1fad3df588d9e82f014"
acorn@^3.0.0, acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@ -264,6 +268,10 @@ ast-types-flow@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
ast-types@0.8.15:
version "0.8.15"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52"
ast-types@0.9.8:
version "0.9.8"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.8.tgz#6cb6a40beba31f49f20928e28439fc14a3dab078"
@ -977,6 +985,10 @@ balanced-match@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
base62@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/base62/-/base62-0.1.1.tgz#7b4174c2f94449753b11c2651c083da841a7b084"
base64-js@^1.0.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
@ -2612,6 +2624,14 @@ es-to-primitive@^1.1.1:
is-date-object "^1.0.1"
is-symbol "^1.0.1"
es3ify@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/es3ify/-/es3ify-0.1.4.tgz#ad9fa5df1ae34f3f31e1211b5818b2d51078dfd1"
dependencies:
esprima-fb "~3001.0001.0000-dev-harmony-fb"
jstransform "~3.0.0"
through "~2.3.4"
es5-ext@^0.10.12, es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@^0.10.9, es5-ext@~0.10.11:
version "0.10.12"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047"
@ -2831,6 +2851,10 @@ eslint@^3.19.0:
text-table "~0.2.0"
user-home "^2.0.0"
esmangle-evaluator@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/esmangle-evaluator/-/esmangle-evaluator-1.0.1.tgz#620d866ef4861b3311f75766d52a8572bb3c6336"
espree@^3.4.0:
version "3.4.2"
resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.2.tgz#38dbdedbedc95b8961a1fbf04734a8f6a9c8c592"
@ -2838,6 +2862,14 @@ espree@^3.4.0:
acorn "^5.0.1"
acorn-jsx "^3.0.0"
esprima-fb@~15001.1001.0-dev-harmony-fb:
version "15001.1001.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659"
esprima-fb@~3001.0001.0000-dev-harmony-fb, esprima-fb@~3001.1.0-dev-harmony-fb:
version "3001.1.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz#b77d37abcd38ea0b77426bb8bc2922ce6b426411"
esprima@^2.6.0, esprima@^2.7.1, esprima@~2.7.0:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
@ -3003,6 +3035,15 @@ extsprintf@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
falafel@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/falafel/-/falafel-1.2.0.tgz#c18d24ef5091174a497f318cd24b026a25cddab4"
dependencies:
acorn "^1.0.3"
foreach "^2.0.5"
isarray "0.0.1"
object-keys "^1.0.6"
fancy-log@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.2.0.tgz#d5a51b53e9ab22ca07d558f2b67ae55fdb5fcbd8"
@ -3980,6 +4021,10 @@ image-to-data-uri@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/image-to-data-uri/-/image-to-data-uri-1.1.0.tgz#23f9d7f17b6562ca6a8145e9779c9a166b829f6e"
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
immutable@^3.7.6, immutable@^3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2"
@ -4060,6 +4105,13 @@ ini@^1.3.4, ini@~1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
inline-process-browser@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/inline-process-browser/-/inline-process-browser-1.0.0.tgz#46a61b153dd3c9b1624b1a00626edb4f7f414f22"
dependencies:
falafel "^1.0.1"
through2 "^0.6.5"
inline-style-prefixer@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7"
@ -4847,6 +4899,14 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.3.6"
jstransform@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-3.0.0.tgz#a2591ab6cee8d97bf3be830dbfa2313b87cd640b"
dependencies:
base62 "0.1.1"
esprima-fb "~3001.1.0-dev-harmony-fb"
source-map "0.1.31"
jsx-ast-utils@^1.0.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.2.tgz#dff658782705352111f9865d40471bc4a955961e"
@ -5068,6 +5128,15 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
lie@3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.0.2.tgz#ffda21d7bba26f377cad865d3649b2fc8ce39fea"
dependencies:
es3ify "^0.1.3"
immediate "~3.0.5"
inline-process-browser "^1.0.0"
unreachable-branch-transform "^0.3.0"
liftoff@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.3.0.tgz#a98f2ff67183d8ba7cfaca10548bd7ff0550b385"
@ -5159,6 +5228,12 @@ loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.14, loader-utils@^0.
json5 "^0.5.0"
object-assign "^4.0.1"
localforage@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.5.0.tgz#6b994e19b56611fa85df3992df397ac4ab66e815"
dependencies:
lie "3.0.2"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@ -6245,7 +6320,7 @@ object-is@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
object-keys@^1.0.10, object-keys@^1.0.8:
object-keys@^1.0.10, object-keys@^1.0.6, object-keys@^1.0.8:
version "1.0.11"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
@ -6968,7 +7043,7 @@ prismjs@^1.6.0:
optionalDependencies:
clipboard "^1.5.5"
private@^0.1.6:
private@^0.1.6, private@~0.1.5:
version "0.1.6"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.6.tgz#55c6a976d0f9bafb9924851350fe47b9b5fbb7c1"
@ -7335,6 +7410,15 @@ readline2@^1.0.1:
is-fullwidth-code-point "^1.0.0"
mute-stream "0.0.5"
recast@^0.10.1:
version "0.10.43"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f"
dependencies:
ast-types "0.8.15"
esprima-fb "~15001.1001.0-dev-harmony-fb"
private "~0.1.5"
source-map "~0.5.0"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
@ -8033,6 +8117,12 @@ source-map-support@^0.4.2:
dependencies:
source-map "^0.5.3"
source-map@0.1.31:
version "0.1.31"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.31.tgz#9f704d0d69d9e138a81badf6ebb4fde33d151c61"
dependencies:
amdefine ">=0.0.4"
source-map@0.1.x:
version "0.1.43"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
@ -8045,7 +8135,7 @@ source-map@0.4.x, source-map@^0.4.4, source-map@~0.4.1:
dependencies:
amdefine ">=0.0.4"
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
@ -8395,7 +8485,7 @@ throat@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-3.0.0.tgz#e7c64c867cbb3845f10877642f7b60055b8ec0d6"
through2@^0.6.1:
through2@^0.6.1, through2@^0.6.2, through2@^0.6.5:
version "0.6.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48"
dependencies:
@ -8409,7 +8499,7 @@ through2@^2.0.0:
readable-stream "~2.0.0"
xtend "~4.0.0"
through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1:
through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.4:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -8665,6 +8755,14 @@ unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
unreachable-branch-transform@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/unreachable-branch-transform/-/unreachable-branch-transform-0.3.0.tgz#d99cc4c6e746d264928845b611db54b0f3474caa"
dependencies:
esmangle-evaluator "^1.0.0"
recast "^0.10.1"
through2 "^0.6.2"
unzip@^0.1.11:
version "0.1.11"
resolved "https://registry.yarnpkg.com/unzip/-/unzip-0.1.11.tgz#89749c63b058d7d90d619f86b98aa1535d3b97f0"