diff --git a/packages/gallery/src/interface/gallery.js b/packages/gallery/src/interface/gallery.js index 12c0619..1d50cc6 100644 --- a/packages/gallery/src/interface/gallery.js +++ b/packages/gallery/src/interface/gallery.js @@ -18,12 +18,10 @@ export function GalleryView(vm, model) { const NAV_OPTIONS = { images: { selector: image.SELECTOR, - watcher: image.watcher, title: 'Images' }, albums: { selector: index.SELECTOR, - watcher: Watcher(db, index.SELECTOR), title: 'Albums' } }; @@ -36,7 +34,7 @@ export function GalleryView(vm, model) { data.cleanup(); } const o = NAV_OPTIONS[route.name]; - data = LiveArray(db, o.selector, o.watcher); + data = LiveArray(db, o.selector); title = o.title; data.subscribe(() => vm.redraw()); }); diff --git a/packages/gallery/src/utils/livearray.js b/packages/gallery/src/utils/livearray.js index 3e09d81..89c4f31 100644 --- a/packages/gallery/src/utils/livearray.js +++ b/packages/gallery/src/utils/livearray.js @@ -6,12 +6,15 @@ import { Watcher } from './watcher.js'; import { pouchDocArrayComparator } from './comparators.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. -// 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. - -const globalWatcher = Watcher(getDatabase(), {}, true); +// The point of the watcher mechanism is that PouchDB.changes doesn't register +// 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 +// 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(); -let globalWatcherSubscription = null; +const dbIDs = new Map(); function checkDocs(id, deleted, doc) { // 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)) { watchingIDs.set(id, new Map()); } 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)) { const idSet = watchingIDs.get(id); idSet.delete(selector); if (idSet.size === 0) { 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. -export function LiveArray(db, selector, watcher) { - const _watcher = watcher || Watcher(db, selector); +export function LiveArray(db, selector, mapper) { + const _watcher = Watcher(db, selector); let changeSub = null; + let _mapper = mapper || (doc => doc); const ready = prop(false); 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 addThisID = id => addID(id, selector, refresh); - const removeThisID = id => removeID(id, selector); + const addThisID = id => addID(db, id, selector, refresh); + const removeThisID = id => removeID(db, id, selector); const cleanup = () => { docs.unsubscribeAll();