hydrogen-web/prototypes/idb-continue-key.html
Bruno Windels 53cdabb459 store method to find events to connect with when filling gaps
as fragments can be unaware of their chronological relationship,
we need to check whether the events received from /messages or /context
already exists, so we can later hook up the fragments.
2019-05-11 13:10:31 +02:00

165 lines
7 KiB
HTML

<html>
<head><meta charset="utf-8"></head>
<body>
<script type="text/javascript">
function reqAsPromise(req) {
return new Promise((resolve, reject) => {
req.onsuccess = () => resolve(req);
req.onerror = (err) => reject(err);
});
}
function txnAsPromise(txn) {
return new Promise((resolve, reject) => {
txn.addEventListener("complete", resolve);
txn.addEventListener("abort", reject);
});
}
function iterateCursor(cursor, processValue) {
// TODO: does cursor already have a value here??
return new Promise((resolve, reject) => {
cursor.onerror = (event) => {
reject(new Error("Query failed: " + event.target.errorCode));
};
// collect results
cursor.onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) {
resolve(false);
return; // end of results
}
const {done, jumpTo} = processValue(cursor.value, cursor.key);
if (done) {
resolve(true);
} else {
cursor.continue(jumpTo);
}
};
});
}
/**
* Checks if a given set of keys exist.
* Calls `callback(key, found)` for each key in `keys`, in an unspecified order.
* If the callback returns true, the search is halted and callback won't be called again.
*/
async function findKeys(target, keys, backwards, callback) {
const direction = backwards ? "prev" : "next";
const compareKeys = (a, b) => backwards ? -indexedDB.cmp(a, b) : indexedDB.cmp(a, b);
const sortedKeys = keys.slice().sort(compareKeys);
console.log(sortedKeys);
const firstKey = backwards ? sortedKeys[sortedKeys.length - 1] : sortedKeys[0];
const lastKey = backwards ? sortedKeys[0] : sortedKeys[sortedKeys.length - 1];
const cursor = target.openKeyCursor(IDBKeyRange.bound(firstKey, lastKey), direction);
let i = 0;
let consumerDone = false;
await iterateCursor(cursor, (value, key) => {
// while key is larger than next key, advance and report false
while(i < sortedKeys.length && compareKeys(sortedKeys[i], key) < 0 && !consumerDone) {
console.log("before match", sortedKeys[i]);
consumerDone = callback(sortedKeys[i], false);
++i;
}
if (i < sortedKeys.length && compareKeys(sortedKeys[i], key) === 0 && !consumerDone) {
console.log("match", sortedKeys[i]);
consumerDone = callback(sortedKeys[i], true);
++i;
}
const done = consumerDone || i >= sortedKeys.length;
const jumpTo = !done && sortedKeys[i];
return {done, jumpTo};
});
// report null for keys we didn't to at the end
while (!consumerDone && i < sortedKeys.length) {
console.log("afterwards", sortedKeys[i]);
consumerDone = callback(sortedKeys[i], false);
++i;
}
}
async function findFirstOrLastOccurringEventId(target, roomId, eventIds, findLast = false) {
const keys = eventIds.map(eventId => [roomId, eventId]);
const results = new Array(keys.length);
let firstFoundEventId;
// find first result that is found and has no undefined results before it
function firstFoundAndPrecedingResolved() {
let inc = findLast ? -1 : 1;
let start = findLast ? results.length - 1 : 0;
for(let i = start; i >= 0 && i < results.length; i += inc) {
if (results[i] === undefined) {
return;
} else if(results[i] === true) {
return keys[i];
}
}
}
await findKeys(target, keys, findLast, (key, found) => {
const index = keys.indexOf(key);
results[index] = found;
firstFoundEventId = firstFoundAndPrecedingResolved();
return !!firstFoundEventId;
});
return firstFoundEventId;
}
(async () => {
let db;
let isNew = false;
// open db
{
const req = window.indexedDB.open("prototype-idb-continue-key");
req.onupgradeneeded = (ev) => {
const db = ev.target.result;
db.createObjectStore("timeline", {keyPath: ["roomId", "eventId"]});
isNew = true;
};
db = (await reqAsPromise(req)).result;
}
const roomId = "!abcdef:localhost";
if (isNew) {
const txn = db.transaction(["timeline"], "readwrite");
const store = txn.objectStore("timeline");
for (var i = 1; i <= 100; ++i) {
store.add({roomId, eventId: `$${i * 10}`});
}
await txnAsPromise(txn);
}
console.log("show all in order we get them");
{
const txn = db.transaction(["timeline"], "readonly");
const store = txn.objectStore("timeline");
const cursor = store.openKeyCursor();
await iterateCursor(cursor, (value, key) => {
console.log(key);
return {done: false};
});
}
console.log("run findKeys");
{
const txn = db.transaction(["timeline"], "readonly");
const store = txn.objectStore("timeline");
const eventIds = ["$992", "$1000", "$1010", "$991", "$500", "$990"];
// const eventIds = ["$992", "$1010"];
const keys = eventIds.map(eventId => [roomId, eventId]);
await findKeys(store, keys, false, (key, found) => {
console.log(key, found);
});
}
console.log("run findFirstOrLastOccurringEventId");
{
const txn = db.transaction(["timeline"], "readonly");
const store = txn.objectStore("timeline");
const eventIds = ["$992", "$1000", "$1010", "$991", "$500", "$990", "$123"];
const firstMatch = await findFirstOrLastOccurringEventId(store, roomId, eventIds, false);
console.log("firstMatch", firstMatch);
const lastMatch = await findFirstOrLastOccurringEventId(store, roomId, eventIds, true);
console.log("lastMatch", lastMatch);
}
})();
</script>
</body>
</html>