forked from mystiq/hydrogen-web
cleanup idb storage
This commit is contained in:
parent
13eef402aa
commit
90300dcdaf
16 changed files with 132 additions and 121 deletions
|
@ -1,5 +1,6 @@
|
|||
import Network from "../network.js";
|
||||
import Session from "../session.js";
|
||||
import Network from "./network.js";
|
||||
import Session from "./session.js";
|
||||
import createIdbStorage from "./storage/idb/factory.js";
|
||||
const HOMESERVER = "http://localhost:8008";
|
||||
|
||||
async function getLoginData(username, password) {
|
||||
|
@ -17,7 +18,7 @@ async function getLoginData(username, password) {
|
|||
async function main() {
|
||||
const loginData = await getLoginData("bruno1", "testtest");
|
||||
const network = new Network(HOMESERVER, loginData.access_token);
|
||||
const storage = new IdbStorage("morpheus_session");
|
||||
const storage = await createIdbStorage("morpheus_session");
|
||||
const session = new Session(loginData, storage);
|
||||
await session.load();
|
||||
const sync = new Sync(network, session, storage);
|
||||
|
|
18
src/storage/idb/factory.js
Normal file
18
src/storage/idb/factory.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
export default async function createIdbStorage(databaseName) {
|
||||
const db = await openDatabase(databaseName, createStores);
|
||||
return new Storage(db);
|
||||
}
|
||||
|
||||
function createStores(db) {
|
||||
db.createObjectStore("sync"); //sync token
|
||||
db.createObjectStore("roomSummary", "room_id", {unique: true});
|
||||
const timeline = db.createObjectStore("roomTimeline", ["room_id", "sort_key"]);
|
||||
timeline.createIndex("by_event_id", ["room_id", "event.event_id"], {unique: true});
|
||||
// how to get the first/last x events for a room?
|
||||
// we don't want to specify the sort key, but would need an index for the room_id?
|
||||
// take sort_key as primary key then and have index on event_id?
|
||||
// still, you also can't have a PK of [room_id, sort_key] and get the last or first events with just the room_id? the only thing that changes it that the PK will provide an inherent sorting that you inherit in an index that only has room_id as keyPath??? There must be a better way, need to write a prototype test for this.
|
||||
// SOLUTION: with numeric keys, you can just us a min/max value to get first/last
|
||||
// db.createObjectStore("members", ["room_id", "state_key"]);
|
||||
const state = db.createObjectStore("roomState", ["event.room_id", "event.type", "event.state_key"]);
|
||||
}
|
11
src/storage/idb/index.js
Normal file
11
src/storage/idb/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import QueryTarget from "./query-target.js";
|
||||
|
||||
export default class Index extends QueryTarget {
|
||||
constructor(index) {
|
||||
this._index = index;
|
||||
}
|
||||
|
||||
_queryTarget() {
|
||||
return this._index;
|
||||
}
|
||||
}
|
|
@ -1,80 +1,6 @@
|
|||
const SYNC_STORES = [
|
||||
"sync",
|
||||
"summary",
|
||||
"timeline",
|
||||
"members",
|
||||
"state"
|
||||
];
|
||||
import {iterateCursor} from "./utils";
|
||||
|
||||
class Database {
|
||||
constructor(idbDatabase) {
|
||||
this._db = idbDatabase;
|
||||
this._syncTxn = null;
|
||||
}
|
||||
|
||||
async startSyncTxn() {
|
||||
const txn = this._db.transaction(SYNC_STORES, "readwrite");
|
||||
return new Transaction(txn, SYNC_STORES);
|
||||
}
|
||||
|
||||
startReadOnlyTxn(storeName) {
|
||||
if (this._syncTxn && SYNC_STORES.includes(storeName)) {
|
||||
return this._syncTxn;
|
||||
} else {
|
||||
return this._db.transaction([storeName], "readonly");
|
||||
}
|
||||
}
|
||||
|
||||
startReadWriteTxn(storeName) {
|
||||
if (this._syncTxn && SYNC_STORES.includes(storeName)) {
|
||||
return this._syncTxn;
|
||||
} else {
|
||||
return this._db.transaction([storeName], "readwrite");
|
||||
}
|
||||
}
|
||||
|
||||
store(storeName) {
|
||||
return new ObjectStore(this, storeName);
|
||||
}
|
||||
}
|
||||
|
||||
class Transaction {
|
||||
constructor(txn, allowedStoreNames) {
|
||||
this._txn = txn;
|
||||
this._stores = {
|
||||
sync: null,
|
||||
summary: null,
|
||||
timeline: null,
|
||||
state: null,
|
||||
};
|
||||
this._allowedStoreNames = allowedStoreNames;
|
||||
}
|
||||
|
||||
_idbStore(name) {
|
||||
if (!this._allowedStoreNames.includes(name)) {
|
||||
throw new Error(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`);
|
||||
}
|
||||
return new ObjectStore(this._txn.getObjectStore(name));
|
||||
}
|
||||
|
||||
get timeline() {
|
||||
if (!this._stores.timeline) {
|
||||
const idbStore = this._idbStore("timeline");
|
||||
this._stores.timeline = new TimelineStore(idbStore);
|
||||
}
|
||||
return this._stores.timeline;
|
||||
}
|
||||
|
||||
complete() {
|
||||
return txnAsPromise(this._txn);
|
||||
}
|
||||
|
||||
abort() {
|
||||
this._txn.abort();
|
||||
}
|
||||
}
|
||||
|
||||
class QueryTarget {
|
||||
export default class QueryTarget {
|
||||
reduce(range, reducer, initialValue) {
|
||||
return this._reduce(range, reducer, initialValue, "next");
|
||||
}
|
||||
|
@ -166,27 +92,3 @@ class QueryTarget {
|
|||
throw new Error("override this");
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectStore extends QueryTarget {
|
||||
constructor(store) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
_queryTarget() {
|
||||
return this._store;
|
||||
}
|
||||
|
||||
index(indexName) {
|
||||
return new Index(this._store.index(indexName));
|
||||
}
|
||||
}
|
||||
|
||||
class Index extends QueryTarget {
|
||||
constructor(index) {
|
||||
this._index = index;
|
||||
}
|
||||
|
||||
_queryTarget() {
|
||||
return this._index;
|
||||
}
|
||||
}
|
31
src/storage/idb/storage.js
Normal file
31
src/storage/idb/storage.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
export const STORE_NAMES = ["sync", "roomState", "roomSummary", "roomTimeline"];
|
||||
|
||||
export default class Storage {
|
||||
constructor(idbDatabase) {
|
||||
this._db = idbDatabase;
|
||||
const nameMap = STORE_NAMES.reduce((nameMap, name) => {
|
||||
nameMap[name] = name;
|
||||
return nameMap;
|
||||
}, {});
|
||||
this.storeNames = Object.freeze(nameMap);
|
||||
}
|
||||
|
||||
_validateStoreNames(storeNames) {
|
||||
const unknownStoreName = storeNames.find(name => !STORE_NAMES.includes(name));
|
||||
if (unknownStoreName) {
|
||||
throw new Error(`Tried to open a transaction for unknown store ${unknownStoreName}`);
|
||||
}
|
||||
}
|
||||
|
||||
startReadOnlyTxn(storeNames) {
|
||||
this._validateStoreNames(storeNames);
|
||||
const txn = this._db.transaction(storeNames, "readonly");
|
||||
return new Transaction(txn, storeNames);
|
||||
}
|
||||
|
||||
startReadWriteTxn(storeNames) {
|
||||
this._validateStoreNames(storeNames);
|
||||
const txn = this._db.transaction(storeNames, "readwrite");
|
||||
return new Transaction(txn, storeNames);
|
||||
}
|
||||
}
|
16
src/storage/idb/store.js
Normal file
16
src/storage/idb/store.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import QueryTarget from "./query-target.js";
|
||||
import Index from "./index.js";
|
||||
|
||||
export default class Store extends QueryTarget {
|
||||
constructor(store) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
_queryTarget() {
|
||||
return this._store;
|
||||
}
|
||||
|
||||
index(indexName) {
|
||||
return new Index(this._store.index(indexName));
|
||||
}
|
||||
}
|
|
@ -6,8 +6,8 @@ function createSessionsStore(db) {
|
|||
|
||||
function createStores(db) {
|
||||
db.createObjectStore("sync"); //sync token
|
||||
db.createObjectStore("summary", "room_id", {unique: true});
|
||||
const timeline = db.createObjectStore("timeline", ["room_id", "sort_key"]);
|
||||
db.createObjectStore("roomSummary", "room_id", {unique: true});
|
||||
const timeline = db.createObjectStore("roomTimeline", ["room_id", "sort_key"]);
|
||||
timeline.createIndex("by_event_id", ["room_id", "event.event_id"], {unique: true});
|
||||
// how to get the first/last x events for a room?
|
||||
// we don't want to specify the sort key, but would need an index for the room_id?
|
||||
|
@ -15,7 +15,7 @@ function createStores(db) {
|
|||
// still, you also can't have a PK of [room_id, sort_key] and get the last or first events with just the room_id? the only thing that changes it that the PK will provide an inherent sorting that you inherit in an index that only has room_id as keyPath??? There must be a better way, need to write a prototype test for this.
|
||||
// SOLUTION: with numeric keys, you can just us a min/max value to get first/last
|
||||
// db.createObjectStore("members", ["room_id", "state_key"]);
|
||||
const state = db.createObjectStore("state", ["event.room_id", "event.type", "event.state_key"]);
|
||||
const state = db.createObjectStore("roomState", ["event.room_id", "event.type", "event.state_key"]);
|
||||
}
|
||||
|
||||
class Sessions {
|
|
@ -1,5 +1,5 @@
|
|||
class StateStore {
|
||||
constructor(roomId, db) {
|
||||
constructor(db) {
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
class SyncTransaction {
|
||||
setSyncToken(syncToken, lastSynced) {
|
||||
|
||||
}
|
||||
|
||||
getRoomStore(roomId) {
|
||||
new RoomStore(new Database(null, this._txn), roomId)
|
||||
}
|
||||
|
||||
result() {
|
||||
return txnAsPromise(this._txn);
|
||||
}
|
||||
}
|
40
src/storage/idb/transaction.js
Normal file
40
src/storage/idb/transaction.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import {txnAsPromise} from "./utils";
|
||||
import Store from "./store.js";
|
||||
import TimelineStore from "./stores/timeline.js";
|
||||
|
||||
export default class Transaction {
|
||||
constructor(txn, allowedStoreNames) {
|
||||
this._txn = txn;
|
||||
this._allowedStoreNames = allowedStoreNames;
|
||||
this._stores = {
|
||||
sync: null,
|
||||
summary: null,
|
||||
timeline: null,
|
||||
state: null,
|
||||
};
|
||||
}
|
||||
|
||||
_store(name) {
|
||||
if (!this._allowedStoreNames.includes(name)) {
|
||||
// more specific error? this is a bug, so maybe not ...
|
||||
throw new Error(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`);
|
||||
}
|
||||
return new Store(this._txn.getObjectStore(name));
|
||||
}
|
||||
|
||||
get timeline() {
|
||||
if (!this._stores.timeline) {
|
||||
const idbStore = this._idbStore("timeline");
|
||||
this._stores.timeline = new TimelineStore(idbStore);
|
||||
}
|
||||
return this._stores.timeline;
|
||||
}
|
||||
|
||||
complete() {
|
||||
return txnAsPromise(this._txn);
|
||||
}
|
||||
|
||||
abort() {
|
||||
this._txn.abort();
|
||||
}
|
||||
}
|
|
@ -62,7 +62,12 @@ export class Sync {
|
|||
this._currentRequest = this._network.sync(timeout, syncToken);
|
||||
const response = await this._currentRequest.response;
|
||||
syncToken = response.next_batch;
|
||||
const txn = this._storage.startSyncTxn();
|
||||
const storeNames = this._storage.storeNames;
|
||||
const txn = this._storage.startReadWriteTxn([
|
||||
storeNames.timeline,
|
||||
storeNames.sync,
|
||||
storeNames.state
|
||||
]);
|
||||
try {
|
||||
session.applySync(syncToken, response.account_data, txn);
|
||||
// to_device
|
||||
|
|
Loading…
Reference in a new issue