Redo LiveArray to watch all returned IDs for removal from the selector.
There is still a big TODO here in that the globalWatcher mechanism assumes one database. Need to re-structure this whole thing around a db instance.
This commit is contained in:
parent
e37679c2c9
commit
faa3c48c99
@ -6,7 +6,6 @@ import { LiveArray } from '../utils/livearray.js';
|
||||
export function AlbumView(vm, model) {
|
||||
const { remove, db } = model;
|
||||
let data = null;
|
||||
let currentMemberLen = -1;
|
||||
let title = null;
|
||||
|
||||
function removeImageFromAlbum(id, rev) {
|
||||
@ -15,14 +14,13 @@ export function AlbumView(vm, model) {
|
||||
|
||||
return function(vm, model, key, opts) {
|
||||
const { doc, remove } = model;
|
||||
const { props, members } = doc;
|
||||
const { props } = doc;
|
||||
|
||||
if (title !== props.title || currentMemberLen !== members.length) {
|
||||
if (title !== props.title) {
|
||||
if (data) {
|
||||
data.cleanup();
|
||||
}
|
||||
title = props.title;
|
||||
currentMemberLen = members.length;
|
||||
const SELECTOR = Object.assign(
|
||||
{
|
||||
[`tags.${title}`]: { $eq: true }
|
||||
|
||||
9
packages/gallery/src/utils/comparators.js
Normal file
9
packages/gallery/src/utils/comparators.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { extractID } from './conversion.js';
|
||||
import { equals } from './set.js';
|
||||
|
||||
export function pouchDocArrayComparator(a, b) {
|
||||
const aIDs = a.map(extractID);
|
||||
const bIDs = b.map(extractID);
|
||||
|
||||
return equals(new Set(...aIDs), new Set(...bIDs));
|
||||
}
|
||||
@ -35,3 +35,7 @@ export function deepAssign(to, ...rest) {
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
export function extractID(doc) {
|
||||
return doc._id;
|
||||
}
|
||||
|
||||
@ -1,35 +1,96 @@
|
||||
import { observable, computed } from 'frptools';
|
||||
import { group, groupEnd, log } from '../services/console.js';
|
||||
import { Watcher } from './watcher.js';
|
||||
import { matchesSelector } from 'pouchdb-selector-core';
|
||||
|
||||
import { getDatabase } from '../services/db.js';
|
||||
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);
|
||||
const watchingIDs = new Map();
|
||||
let globalWatcherSubscription = null;
|
||||
|
||||
function checkDocs(id, deleted, doc) {
|
||||
// Is the changed doc one that we're watching?
|
||||
if (watchingIDs.has(id)) {
|
||||
const refresherMap = watchingIDs.get(id);
|
||||
// if the doc doesn't match a watching selector, then refresh its LA
|
||||
[...refresherMap.keys()]
|
||||
.filter(s => !matchesSelector(doc, s))
|
||||
.forEach(s => refresherMap.get(s)());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function addID(id, selector, refresher) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
const data = observable({ docs: [] });
|
||||
const docs = computed(r => r.docs, [data]);
|
||||
let changeSub = null;
|
||||
|
||||
const accessor = docs;
|
||||
accessor.ready = observable(false);
|
||||
accessor.cleanup = () => {
|
||||
docs.detach();
|
||||
const ready = observable(false);
|
||||
const data = observable({ docs: [] });
|
||||
const docs = computed(r => r.docs, [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 cleanup = () => {
|
||||
docs.unsubscribeAll();
|
||||
ready.unsubscribeAll();
|
||||
if (changeSub) {
|
||||
changeSub();
|
||||
changeSub = null;
|
||||
}
|
||||
accessor.ready.unsubscribeAll();
|
||||
[...idSet()].forEach(removeThisID);
|
||||
data({ docs: [] });
|
||||
};
|
||||
|
||||
async function refresh() {
|
||||
group('LiveArray Refreshing');
|
||||
log(selector);
|
||||
const refresh = async function refresh() {
|
||||
const oldIdSet = idSet();
|
||||
data(await db.find({ selector }));
|
||||
log(data());
|
||||
groupEnd('LiveArray Refreshing');
|
||||
}
|
||||
const currentIDSet = idSet();
|
||||
// Removes IDs not in the new set
|
||||
[...difference(oldIdSet, currentIDSet)].forEach(removeThisID);
|
||||
// Add IDs in the new set
|
||||
[...difference(currentIDSet, oldIdSet)].forEach(addThisID);
|
||||
};
|
||||
|
||||
docs.ready = ready;
|
||||
docs.cleanup = cleanup;
|
||||
docs.selector = selector;
|
||||
docs.db = db;
|
||||
|
||||
refresh().then(() => {
|
||||
changeSub = _watcher(refresh);
|
||||
accessor.ready(true);
|
||||
ready(true);
|
||||
});
|
||||
return accessor;
|
||||
return docs;
|
||||
}
|
||||
|
||||
14
packages/gallery/src/utils/set.js
Normal file
14
packages/gallery/src/utils/set.js
Normal file
@ -0,0 +1,14 @@
|
||||
export function equals(a, b) {
|
||||
return (
|
||||
[...a].reduce((acc, d) => acc && b.has(d), true) &&
|
||||
[...b].reduce((acc, d) => acc && a.has(d), true)
|
||||
);
|
||||
}
|
||||
|
||||
export function intersection(a, b) {
|
||||
return new Set([...a].filter(x => b.has(x)));
|
||||
}
|
||||
|
||||
export function difference(a, b) {
|
||||
return new Set([...a].filter(x => !b.has(x)));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user