wip
This commit is contained in:
parent
d87444824d
commit
952f1abddf
7 changed files with 308 additions and 7 deletions
15
.editorconfig
Normal file
15
.editorconfig
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
# [*.{js,py}]
|
|
@ -3,7 +3,7 @@ A javascript matrix client prototype, trying to minize RAM usage by offloading a
|
|||
|
||||
## Status
|
||||
|
||||
Sort of syncing, but not really yet.
|
||||
Syncing & storing rooms with state and timeline, next step is building minimal UI
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
@ -14,3 +14,4 @@ You need to disable the browser cache to see your updated code when refreshing
|
|||
- store all fetched messages, not just synced ones
|
||||
- fast local search (with words index)
|
||||
- scroll timeline with date tooltip?
|
||||
- jump to timestamp
|
14
doc/TODO.md
14
doc/TODO.md
|
@ -5,12 +5,12 @@
|
|||
- DONE: add eventemitter
|
||||
- DONE: make sync work
|
||||
- DONE: store summaries
|
||||
- setup editorconfig
|
||||
- DONE: setup editorconfig
|
||||
- DONE: setup linting (also in editor)
|
||||
- DONE: store timeline
|
||||
- DONE: store state
|
||||
- make summary work better (name and joined/inviteCount doesn't seem to work well)
|
||||
- timeline doesn't seem to recover it's key well upon loading, the query in load seems to never yield an event in the persister
|
||||
- DONE: make summary work better (name and joined/inviteCount doesn't seem to work well)
|
||||
- DONE: timeline doesn't seem to recover it's key well upon loading, the query in load seems to never yield an event in the persister
|
||||
- map DOMException to something better
|
||||
- it's pretty opaque now when something idb related fails. DOMException has these fields:
|
||||
code: 0
|
||||
|
@ -21,4 +21,10 @@
|
|||
- build a very basic interface with
|
||||
- a start/stop sync button
|
||||
- a room list sorted alphabetically
|
||||
- clicking on a room list, you see messages (userId -> body)
|
||||
- clicking on a room list, you see messages (userId -> body)
|
||||
- send messages
|
||||
- fill gaps with call to /messages
|
||||
- create sync filter
|
||||
- lazy loading members
|
||||
- decide denormalized data in summary vs reading from multiple stores PER room on load
|
||||
- allow Room/Summary class to be subclassed and store additional data?
|
||||
|
|
74
prototypes/chrome-keys.html
Normal file
74
prototypes/chrome-keys.html
Normal file
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
function reqAsPromise(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
req.addEventListener("success", event => resolve(event.target.result));
|
||||
req.addEventListener("error", event => reject(event.target.error));
|
||||
});
|
||||
}
|
||||
|
||||
function txnAsPromise(txn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
txn.addEventListener("complete", resolve);
|
||||
txn.addEventListener("abort", reject);
|
||||
});
|
||||
}
|
||||
|
||||
export function readAll(cursor) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const results = [];
|
||||
cursor.onerror = (event) => {
|
||||
reject(new Error("Query failed: " + event.target.errorCode));
|
||||
};
|
||||
cursor.onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
resolve(results);
|
||||
} else {
|
||||
results.push(cursor.value);
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let isNew = false;
|
||||
const openReq = window.indexedDB.open("composed_binary_keys_test", 1);
|
||||
openReq.onupgradeneeded = (ev) => {
|
||||
const db = ev.target.result;
|
||||
db.createObjectStore("test", {keyPath: ["strKey", "binKey"]});
|
||||
isNew = true;
|
||||
};
|
||||
const db = await reqAsPromise(openReq);
|
||||
// fill test store with some initial data
|
||||
if (isNew) {
|
||||
const txn = db.transaction("test", "readwrite");
|
||||
const store = txn.objectStore("test");
|
||||
store.add({strKey: "abc", binKey: new Uint8Array([0x0F, 0x00, 0x10, 0x00]).buffer});
|
||||
store.add({strKey: "def", binKey: new Uint8Array([0x00, 0x10, 0x00, 0x00]).buffer});
|
||||
store.add({strKey: "def", binKey: new Uint8Array([0xFF, 0x10, 0x00, 0x00]).buffer});
|
||||
store.add({strKey: "ghi", binKey: new Uint8Array([0x00, 0x00, 0x40, 0x00]).buffer});
|
||||
await txnAsPromise(txn);
|
||||
}
|
||||
|
||||
const txn = db.transaction("test", "readonly");
|
||||
const store = txn.objectStore("test");
|
||||
const defRange = IDBKeyRange.bound(
|
||||
["def", new Uint8Array([0x00, 0x00, 0x00, 0x00]).buffer],
|
||||
["def", new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]).buffer],
|
||||
true, true,
|
||||
);
|
||||
const defValues = await readAll(store.openCursor(defRange, "prev"));
|
||||
console.log(defValues);
|
||||
}
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
102
prototypes/chrome-keys2.html
Normal file
102
prototypes/chrome-keys2.html
Normal file
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
function reqAsPromise(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
req.addEventListener("success", event => resolve(event.target.result));
|
||||
req.addEventListener("error", event => reject(event.target.error));
|
||||
});
|
||||
}
|
||||
|
||||
function txnAsPromise(txn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
txn.addEventListener("complete", resolve);
|
||||
txn.addEventListener("abort", reject);
|
||||
});
|
||||
}
|
||||
|
||||
function readAll(cursor) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const results = [];
|
||||
cursor.onerror = (event) => {
|
||||
reject(new Error("Query failed: " + event.target.errorCode));
|
||||
};
|
||||
cursor.onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
resolve(results);
|
||||
} else {
|
||||
results.push(cursor.value);
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let seed = 13423;
|
||||
function prn() {
|
||||
let x = Math.sin(seed++) * 10000;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
|
||||
function pad(str, len) {
|
||||
return str + " ".repeat(len - str.length);
|
||||
}
|
||||
|
||||
function formatByteArray(a) {
|
||||
return `[${Array.from(a).join(",")}]`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let isNew = false;
|
||||
const openReq = window.indexedDB.open("bin_key_sorting_test", 1);
|
||||
openReq.onupgradeneeded = (ev) => {
|
||||
const db = ev.target.result;
|
||||
db.createObjectStore("test", {keyPath: ["binKey"]});
|
||||
isNew = true;
|
||||
};
|
||||
const db = await reqAsPromise(openReq);
|
||||
// fill test store with some data
|
||||
if (isNew) {
|
||||
const txn = db.transaction("test", "readwrite");
|
||||
const store = txn.objectStore("test");
|
||||
const rndByte = () => Math.ceil(prn() * 255);
|
||||
for(let i = 0; i < 10; ++i) {
|
||||
const b1 = rndByte(), b2 = rndByte(), b3 = rndByte(), b4 = rndByte();
|
||||
console.log(`adding key (${b1},${b2},${b3},${b4})`);
|
||||
store.add({binKey: new Uint8Array([b1, b2, b3, b4])});
|
||||
}
|
||||
store.add({binKey: new Uint8Array([0x80, 0x00, 0x00, 0x00])});
|
||||
store.add({binKey: new Uint8Array([0x00, 0x00, 0x00, 0x00])});
|
||||
store.add({binKey: new Uint8Array([0x7F, 0xFF, 0xFF, 0xFF])});
|
||||
store.add({binKey: new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF])});
|
||||
await txnAsPromise(txn);
|
||||
}
|
||||
|
||||
const txn = db.transaction("test", "readonly");
|
||||
const store = txn.objectStore("test");
|
||||
const range = IDBKeyRange.bound(
|
||||
Uint8Array.from([0x00, 0x00, 0x00, 0x00]).buffer,
|
||||
Uint8Array.from([0xFF, 0xFF, 0xFF, 0xFF]).buffer,
|
||||
// new Uint8Array([0x80, 0x00, 0x00, 0x00]).buffer, new
|
||||
// Uint8Array([0x7F, 0xFF, 0xFF, 0xFF]).buffer,
|
||||
);
|
||||
// const values = await readAll(store.openCursor(range, "next"));
|
||||
const values = await readAll(store.openCursor());
|
||||
|
||||
console.log(pad(`[uint8;4]`, 20) + " [int8;4]");
|
||||
for (const v of values) {
|
||||
const uintStr = formatByteArray(new Uint8Array(v.binKey));
|
||||
const intStr = formatByteArray(new Int8Array(v.binKey));
|
||||
console.log(pad(uintStr, 20) + " " + intStr);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
103
prototypes/chrome-keys3.html
Normal file
103
prototypes/chrome-keys3.html
Normal file
|
@ -0,0 +1,103 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
function reqAsPromise(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
req.addEventListener("success", event => resolve(event.target.result));
|
||||
req.addEventListener("error", event => reject(event.target.error));
|
||||
});
|
||||
}
|
||||
|
||||
function txnAsPromise(txn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
txn.addEventListener("complete", resolve);
|
||||
txn.addEventListener("abort", reject);
|
||||
});
|
||||
}
|
||||
|
||||
function readAll(cursor) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const results = [];
|
||||
cursor.onerror = (event) => {
|
||||
reject(new Error("Query failed: " + event.target.errorCode));
|
||||
};
|
||||
cursor.onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
resolve(results);
|
||||
} else {
|
||||
results.push(cursor.value);
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let seed = 13423;
|
||||
function prn() {
|
||||
let x = Math.sin(seed++) * 10000;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
|
||||
function pad(str, len) {
|
||||
return str + " ".repeat(len - str.length);
|
||||
}
|
||||
|
||||
function formatByteArray(a) {
|
||||
return `[${Array.from(a).join(",")}]`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let isNew = false;
|
||||
const openReq = window.indexedDB.open("bin_key_sorting_test", 1);
|
||||
openReq.onupgradeneeded = (ev) => {
|
||||
const db = ev.target.result;
|
||||
db.createObjectStore("test", {keyPath: ["binKey"]});
|
||||
isNew = true;
|
||||
};
|
||||
const db = await reqAsPromise(openReq);
|
||||
// fill test store with some data
|
||||
if (isNew) {
|
||||
const txn = db.transaction("test", "readwrite");
|
||||
const store = txn.objectStore("test");
|
||||
const rndByte = () => Math.ceil(prn() * 255);
|
||||
for(let i = 0; i < 10; ++i) {
|
||||
const b1 = rndByte(), b2 = rndByte(), b3 = rndByte(), b4 = rndByte();
|
||||
console.log(`adding key (${b1},${b2},${b3},${b4})`);
|
||||
store.add({binKey: new Uint8Array([b1, b2, b3, b4])});
|
||||
}
|
||||
store.add({binKey: new Uint8Array([0x80, 0x00, 0x00, 0x00])});
|
||||
store.add({binKey: new Uint8Array([0x00, 0x00, 0x00, 0x00])});
|
||||
store.add({binKey: new Uint8Array([0x7F, 0xFF, 0xFF, 0xFF])});
|
||||
store.add({binKey: new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF])});
|
||||
await txnAsPromise(txn);
|
||||
}
|
||||
|
||||
const txn = db.transaction("test", "readonly");
|
||||
const store = txn.objectStore("test");
|
||||
const range = IDBKeyRange.upperBound(
|
||||
// Uint8Array.from([0x00, 0x00, 0x00, 0x00]).buffer,
|
||||
Uint8Array.from([0xFF, 0xFF, 0xFF, 0xFF]).buffer,
|
||||
// new Uint8Array([0x80, 0x00, 0x00, 0x00]).buffer, new
|
||||
// Uint8Array([0x7F, 0xFF, 0xFF, 0xFF]).buffer,
|
||||
false
|
||||
);
|
||||
// const values = await readAll(store.openCursor(range, "next"));
|
||||
const values = await readAll(store.openCursor(range, "prev"));
|
||||
|
||||
console.log(pad(`[uint8;4]`, 20) + " [int8;4]");
|
||||
for (const v of values) {
|
||||
const uintStr = formatByteArray(new Uint8Array(v.binKey));
|
||||
const intStr = formatByteArray(new Int8Array(v.binKey));
|
||||
console.log(pad(uintStr, 20) + " " + intStr);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -51,8 +51,8 @@ export default class RoomTimelineStore {
|
|||
gap: null,
|
||||
});
|
||||
}
|
||||
|
||||
async removeEvent(roomId, sortKey) {
|
||||
// could be gap or event
|
||||
async removeEntry(roomId, sortKey) {
|
||||
this._timelineStore.delete([roomId, sortKey.buffer]);
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue