LiveArray can manage it's own watchers (across dbs even)

This commit is contained in:
Timothy Farrell 2017-11-13 21:46:38 -06:00
parent 8c138571c2
commit 5c433f1231
2 changed files with 34 additions and 22 deletions

View File

@ -18,12 +18,10 @@ export function GalleryView(vm, model) {
const NAV_OPTIONS = { const NAV_OPTIONS = {
images: { images: {
selector: image.SELECTOR, selector: image.SELECTOR,
watcher: image.watcher,
title: 'Images' title: 'Images'
}, },
albums: { albums: {
selector: index.SELECTOR, selector: index.SELECTOR,
watcher: Watcher(db, index.SELECTOR),
title: 'Albums' title: 'Albums'
} }
}; };
@ -36,7 +34,7 @@ export function GalleryView(vm, model) {
data.cleanup(); data.cleanup();
} }
const o = NAV_OPTIONS[route.name]; const o = NAV_OPTIONS[route.name];
data = LiveArray(db, o.selector, o.watcher); data = LiveArray(db, o.selector);
title = o.title; title = o.title;
data.subscribe(() => vm.redraw()); data.subscribe(() => vm.redraw());
}); });

View File

@ -6,12 +6,15 @@ import { Watcher } from './watcher.js';
import { pouchDocArrayComparator } from './comparators.js'; import { pouchDocArrayComparator } from './comparators.js';
import { difference } from './set.js'; import { difference } from './set.js';
// The point of the globalWatcher mechanism is that PouchDB.changes doesn't register when a document changes in such a way that removes it from the selector specifications. // The point of the watcher mechanism is that PouchDB.changes doesn't register
// For Example: a selector looks for images with a specific tag. If a change removes that tag, the changes API will not register a change event. globalWatcher watches the document IDs for exactly this type of change and triggers the LiveArray to refresh. // when a document changes in such a way that removes it from the selector
// specifications. For Example: a selector looks for images with a specific
const globalWatcher = Watcher(getDatabase(), {}, true); // tag. If a change removes that tag, the changes API will not register a change
// event. globalWatcher watches the document IDs for exactly this type of
// change and triggers the LiveArray to refresh.
const watcherMap = new Map();
const watchingIDs = new Map(); const watchingIDs = new Map();
let globalWatcherSubscription = null; const dbIDs = new Map();
function checkDocs(id, deleted, doc) { function checkDocs(id, deleted, doc) {
// Is the changed doc one that we're watching? // Is the changed doc one that we're watching?
@ -25,42 +28,53 @@ function checkDocs(id, deleted, doc) {
} }
} }
function addID(id, selector, refresher) { function addID(db, id, selector, refresher) {
if (!watcherMap.has(db)) {
watcherMap.set(db, Watcher(db, {}, true)(checkDocs));
}
if (!dbIDs.has(db)) {
dbIDs.set(db, new Set());
}
dbIDs.get(db).add(id);
if (!watchingIDs.has(id)) { if (!watchingIDs.has(id)) {
watchingIDs.set(id, new Map()); watchingIDs.set(id, new Map());
} }
watchingIDs.get(id).set(selector, refresher); watchingIDs.get(id).set(selector, refresher);
if (globalWatcherSubscription === null) {
globalWatcherSubscription = globalWatcher(checkDocs);
}
} }
function removeID(id, selector) { function removeID(db, id, selector) {
if (watchingIDs.has(id)) { if (watchingIDs.has(id)) {
const idSet = watchingIDs.get(id); const idSet = watchingIDs.get(id);
idSet.delete(selector); idSet.delete(selector);
if (idSet.size === 0) { if (idSet.size === 0) {
watchingIDs.delete(selector); watchingIDs.delete(selector);
if (watchingIDs.size === 0) {
globalWatcherSubscription();
globalWatcherSubscription = null;
} }
const dbIDMap = dbIDs.get(db);
dbIDMap.delete(id);
if (dbIDMap.size === 0) {
// Unsubscribe from this watcher
watcherMap.get(db)();
dbIDs.delete(db);
} }
} }
} }
// LiveArray is a subscribable property function that always returns the db results that match the provided selector and calls subscribers when the results change. // LiveArray is a subscribable property function that always returns the db results that match the provided selector and calls subscribers when the results change.
export function LiveArray(db, selector, watcher) { export function LiveArray(db, selector, mapper) {
const _watcher = watcher || Watcher(db, selector); const _watcher = Watcher(db, selector);
let changeSub = null; let changeSub = null;
let _mapper = mapper || (doc => doc);
const ready = prop(false); const ready = prop(false);
const data = prop({ docs: [] }); const data = prop({ docs: [] });
const docs = computed(r => r.docs, [data], pouchDocArrayComparator); const docs = computed(r => r.docs.map(_mapper), [data], pouchDocArrayComparator);
const idSet = () => docs().reduce((acc, d) => acc.add(d._id), new Set()); const idSet = () => docs().reduce((acc, d) => acc.add(d._id), new Set());
const addThisID = id => addID(id, selector, refresh); const addThisID = id => addID(db, id, selector, refresh);
const removeThisID = id => removeID(id, selector); const removeThisID = id => removeID(db, id, selector);
const cleanup = () => { const cleanup = () => {
docs.unsubscribeAll(); docs.unsubscribeAll();