diff --git a/src/matrix/storage/idb/Store.ts b/src/matrix/storage/idb/Store.ts index 8063a4c8..e2da0707 100644 --- a/src/matrix/storage/idb/Store.ts +++ b/src/matrix/storage/idb/Store.ts @@ -148,7 +148,7 @@ export class Store extends QueryTarget { return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName))); } - put(value: T): Promise { + put(value: T): void { // If this request fails, the error will bubble up to the transaction and abort it, // which is the behaviour we want. Therefore, it is ok to not create a promise for this // request and await it. @@ -159,12 +159,12 @@ export class Store extends QueryTarget { // // Note that this can still throw synchronously, like it does for TransactionInactiveError, // see https://www.w3.org/TR/IndexedDB-2/#transaction-lifetime-concept - return reqAsPromise(this._idbStore.put(value)); + this._idbStore.put(value); } - add(value: T): Promise { + add(value: T): void { // ok to not monitor result of request, see comment in `put`. - return reqAsPromise(this._idbStore.add(value)); + this._idbStore.add(value); } delete(keyOrKeyRange: IDBValidKey | IDBKeyRange): Promise { diff --git a/src/matrix/storage/idb/Transaction.js b/src/matrix/storage/idb/Transaction.js index 4c543f4c..71513730 100644 --- a/src/matrix/storage/idb/Transaction.js +++ b/src/matrix/storage/idb/Transaction.js @@ -17,13 +17,13 @@ limitations under the License. import {txnAsPromise} from "./utils"; import {StorageError} from "../common"; import {Store} from "./Store"; -import {SessionStore} from "./stores/SessionStore.js"; -import {RoomSummaryStore} from "./stores/RoomSummaryStore.js"; -import {InviteStore} from "./stores/InviteStore.js"; -import {TimelineEventStore} from "./stores/TimelineEventStore.js"; -import {TimelineRelationStore} from "./stores/TimelineRelationStore.js"; +import {SessionStore} from "./stores/SessionStore"; +import {RoomSummaryStore} from "./stores/RoomSummaryStore"; +import {InviteStore} from "./stores/InviteStore"; +import {TimelineEventStore} from "./stores/TimelineEventStore"; +import {TimelineRelationStore} from "./stores/TimelineRelationStore"; import {RoomStateStore} from "./stores/RoomStateStore.js"; -import {RoomMemberStore} from "./stores/RoomMemberStore.js"; +import {RoomMemberStore} from "./stores/RoomMemberStore"; import {TimelineFragmentStore} from "./stores/TimelineFragmentStore.js"; import {PendingEventStore} from "./stores/PendingEventStore.js"; import {UserIdentityStore} from "./stores/UserIdentityStore.js"; diff --git a/src/matrix/storage/idb/schema.js b/src/matrix/storage/idb/schema.js index 4915f3a0..50e298d7 100644 --- a/src/matrix/storage/idb/schema.js +++ b/src/matrix/storage/idb/schema.js @@ -1,10 +1,10 @@ import {iterateCursor, reqAsPromise} from "./utils"; import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js"; import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js"; -import {RoomMemberStore} from "./stores/RoomMemberStore.js"; -import {SessionStore} from "./stores/SessionStore.js"; +import {RoomMemberStore} from "./stores/RoomMemberStore"; +import {SessionStore} from "./stores/SessionStore"; import {encodeScopeTypeKey} from "./stores/OperationStore.js"; -import {MAX_UNICODE} from "./stores/common.js"; +import {MAX_UNICODE} from "./stores/common"; // FUNCTIONS SHOULD ONLY BE APPENDED!! // the index in the array is the database version diff --git a/src/matrix/storage/idb/stores/DeviceIdentityStore.js b/src/matrix/storage/idb/stores/DeviceIdentityStore.js index bfc5b30b..207cfb20 100644 --- a/src/matrix/storage/idb/stores/DeviceIdentityStore.js +++ b/src/matrix/storage/idb/stores/DeviceIdentityStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MAX_UNICODE, MIN_UNICODE} from "./common.js"; +import {MAX_UNICODE, MIN_UNICODE} from "./common"; function encodeKey(userId, deviceId) { return `${userId}|${deviceId}`; diff --git a/src/matrix/storage/idb/stores/GroupSessionDecryptionStore.js b/src/matrix/storage/idb/stores/GroupSessionDecryptionStore.js index f3a721f1..da47639d 100644 --- a/src/matrix/storage/idb/stores/GroupSessionDecryptionStore.js +++ b/src/matrix/storage/idb/stores/GroupSessionDecryptionStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MIN_UNICODE, MAX_UNICODE} from "./common.js"; +import {MIN_UNICODE, MAX_UNICODE} from "./common"; function encodeKey(roomId, sessionId, messageIndex) { return `${roomId}|${sessionId}|${messageIndex}`; diff --git a/src/matrix/storage/idb/stores/InboundGroupSessionStore.js b/src/matrix/storage/idb/stores/InboundGroupSessionStore.js index f5dcdc39..488f2510 100644 --- a/src/matrix/storage/idb/stores/InboundGroupSessionStore.js +++ b/src/matrix/storage/idb/stores/InboundGroupSessionStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MIN_UNICODE, MAX_UNICODE} from "./common.js"; +import {MIN_UNICODE, MAX_UNICODE} from "./common"; function encodeKey(roomId, senderKey, sessionId) { return `${roomId}|${senderKey}|${sessionId}`; diff --git a/src/matrix/storage/idb/stores/InviteStore.ts b/src/matrix/storage/idb/stores/InviteStore.ts new file mode 100644 index 00000000..7616ebf7 --- /dev/null +++ b/src/matrix/storage/idb/stores/InviteStore.ts @@ -0,0 +1,51 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import {Store} from "../Store"; +import {MemberData} from "./RoomMemberStore"; + +// TODO: Move to Invite when that's TypeScript. +export interface InviteData { + roomId: string; + isEncrypted: boolean; + isDirectMessage: boolean; + name?: string; + avatarUrl?: string; + avatarColorId: number; + canonicalAlias?: string; + timestamp: number; + joinRule: string; + inviter?: MemberData; +} + +export class InviteStore { + private _inviteStore: Store; + + constructor(inviteStore: Store) { + this._inviteStore = inviteStore; + } + + getAll(): Promise { + return this._inviteStore.selectAll(); + } + + set(invite: InviteData): void { + this._inviteStore.put(invite); + } + + remove(roomId: string): void { + this._inviteStore.delete(roomId); + } +} diff --git a/src/matrix/storage/idb/stores/OperationStore.js b/src/matrix/storage/idb/stores/OperationStore.js index e2040e30..961e36af 100644 --- a/src/matrix/storage/idb/stores/OperationStore.js +++ b/src/matrix/storage/idb/stores/OperationStore.js @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import {MIN_UNICODE, MAX_UNICODE} from "./common.js"; +import {MIN_UNICODE, MAX_UNICODE} from "./common"; export function encodeScopeTypeKey(scope, type) { return `${scope}|${type}`; diff --git a/src/matrix/storage/idb/stores/RoomMemberStore.js b/src/matrix/storage/idb/stores/RoomMemberStore.ts similarity index 61% rename from src/matrix/storage/idb/stores/RoomMemberStore.js rename to src/matrix/storage/idb/stores/RoomMemberStore.ts index f900ff0b..e389bb77 100644 --- a/src/matrix/storage/idb/stores/RoomMemberStore.js +++ b/src/matrix/storage/idb/stores/RoomMemberStore.ts @@ -15,44 +15,59 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MAX_UNICODE} from "./common.js"; +import {MAX_UNICODE} from "./common"; +import {Store} from "../Store"; -function encodeKey(roomId, userId) { +function encodeKey(roomId: string, userId: string) { return `${roomId}|${userId}`; } -function decodeKey(key) { +function decodeKey(key: string): { roomId: string, userId: string } { const [roomId, userId] = key.split("|"); return {roomId, userId}; } +// TODO: Move to RoomMember when that's TypeScript. +export interface MemberData { + roomId: string; + userId: string; + avatarUrl: string; + displayName: string; + membership: "join" | "leave" | "invite" | "ban"; +} + +type MemberStorageEntry = MemberData & { key: string } + // no historical members export class RoomMemberStore { - constructor(roomMembersStore) { + private _roomMembersStore: Store; + + constructor(roomMembersStore: Store) { this._roomMembersStore = roomMembersStore; } - get(roomId, userId) { + get(roomId: string, userId: string): Promise { return this._roomMembersStore.get(encodeKey(roomId, userId)); - } + } - async set(member) { - member.key = encodeKey(member.roomId, member.userId); - return this._roomMembersStore.put(member); - } + set(member: MemberData): void { + // Object.assign would be more typesafe, but small objects + (member as any).key = encodeKey(member.roomId, member.userId); + this._roomMembersStore.put(member as MemberStorageEntry); + } - getAll(roomId) { + getAll(roomId: string): Promise { const range = this._roomMembersStore.IDBKeyRange.lowerBound(encodeKey(roomId, "")); return this._roomMembersStore.selectWhile(range, member => { return member.roomId === roomId; }); } - async getAllUserIds(roomId) { - const userIds = []; + async getAllUserIds(roomId: string): Promise { + const userIds: string[] = []; const range = this._roomMembersStore.IDBKeyRange.lowerBound(encodeKey(roomId, "")); await this._roomMembersStore.iterateKeys(range, key => { - const decodedKey = decodeKey(key); + const decodedKey = decodeKey(key as string); // prevent running into the next room if (decodedKey.roomId === roomId) { userIds.push(decodedKey.userId); @@ -63,7 +78,7 @@ export class RoomMemberStore { return userIds; } - removeAllForRoom(roomId) { + removeAllForRoom(roomId: string): void { // exclude both keys as they are theoretical min and max, // but we should't have a match for just the room id, or room id with max const range = this._roomMembersStore.IDBKeyRange.bound(roomId, `${roomId}|${MAX_UNICODE}`, true, true); diff --git a/src/matrix/storage/idb/stores/RoomStateStore.js b/src/matrix/storage/idb/stores/RoomStateStore.js index 99fc23f7..db54458f 100644 --- a/src/matrix/storage/idb/stores/RoomStateStore.js +++ b/src/matrix/storage/idb/stores/RoomStateStore.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MAX_UNICODE} from "./common.js"; +import {MAX_UNICODE} from "./common"; function encodeKey(roomId, eventType, stateKey) { return `${roomId}|${eventType}|${stateKey}`; diff --git a/src/matrix/storage/idb/stores/RoomSummaryStore.js b/src/matrix/storage/idb/stores/RoomSummaryStore.ts similarity index 57% rename from src/matrix/storage/idb/stores/RoomSummaryStore.js rename to src/matrix/storage/idb/stores/RoomSummaryStore.ts index 426a01bb..bd911572 100644 --- a/src/matrix/storage/idb/stores/RoomSummaryStore.js +++ b/src/matrix/storage/idb/stores/RoomSummaryStore.ts @@ -27,31 +27,35 @@ store contains: inviteCount joinCount */ +import {Store} from "../Store"; +import {SummaryData} from "../../../room/RoomSummary"; /** Used for both roomSummary and archivedRoomSummary stores */ export class RoomSummaryStore { - constructor(summaryStore) { - this._summaryStore = summaryStore; - } + private _summaryStore: Store; - getAll() { - return this._summaryStore.selectAll(); - } + constructor(summaryStore: Store) { + this._summaryStore = summaryStore; + } - set(summary) { - return this._summaryStore.put(summary); - } + getAll(): Promise { + return this._summaryStore.selectAll(); + } - get(roomId) { - return this._summaryStore.get(roomId); - } + set(summary: SummaryData): void { + this._summaryStore.put(summary); + } - async has(roomId) { + get(roomId: string): Promise { + return this._summaryStore.get(roomId); + } + + async has(roomId: string): Promise { const fetchedKey = await this._summaryStore.getKey(roomId); return roomId === fetchedKey; - } + } - remove(roomId) { - return this._summaryStore.delete(roomId); - } + remove(roomId: string): Promise { + return this._summaryStore.delete(roomId); + } } diff --git a/src/matrix/storage/idb/stores/SessionStore.js b/src/matrix/storage/idb/stores/SessionStore.ts similarity index 55% rename from src/matrix/storage/idb/stores/SessionStore.js rename to src/matrix/storage/idb/stores/SessionStore.ts index 3be8e875..859d3319 100644 --- a/src/matrix/storage/idb/stores/SessionStore.js +++ b/src/matrix/storage/idb/stores/SessionStore.ts @@ -13,28 +13,36 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import {Store} from "../Store"; + +export interface SessionEntry { + key: string; + value: any; +} export class SessionStore { - constructor(sessionStore) { - this._sessionStore = sessionStore; - } + private _sessionStore: Store - async get(key) { - const entry = await this._sessionStore.get(key); - if (entry) { - return entry.value; - } - } + constructor(sessionStore: Store) { + this._sessionStore = sessionStore; + } - set(key, value) { - this._sessionStore.put({key, value}); - } + async get(key: string): Promise { + const entry = await this._sessionStore.get(key); + if (entry) { + return entry.value; + } + } - add(key, value) { + set(key: string, value: any): void { + this._sessionStore.put({key, value}); + } + + add(key: string, value: any): void { this._sessionStore.add({key, value}); } - remove(key) { + remove(key: string): void { this._sessionStore.delete(key); } } diff --git a/src/matrix/storage/idb/stores/TimelineEventStore.js b/src/matrix/storage/idb/stores/TimelineEventStore.ts similarity index 65% rename from src/matrix/storage/idb/stores/TimelineEventStore.js rename to src/matrix/storage/idb/stores/TimelineEventStore.ts index 8ff445f0..eb4c70cc 100644 --- a/src/matrix/storage/idb/stores/TimelineEventStore.js +++ b/src/matrix/storage/idb/stores/TimelineEventStore.ts @@ -18,23 +18,50 @@ import {EventKey} from "../../../room/timeline/EventKey.js"; import { StorageError } from "../../common"; import { encodeUint32 } from "../utils"; import {KeyLimits} from "../../common"; +import {Store} from "../Store"; +import {TimelineEvent, StateEvent} from "../../types"; -function encodeKey(roomId, fragmentId, eventIndex) { +interface Annotation { + count: number; + me: boolean; + firstTimestamp: number; +} + +interface StorageEntry { + roomId: string; + fragmentId: number; + eventIndex: number; + event: TimelineEvent | StateEvent; + displayName?: string; + avatarUrl?: string; + annotations?: { [key : string]: Annotation }; + key: string; + eventIdKey: string; +} + +function encodeKey(roomId: string, fragmentId: number, eventIndex: number): string { return `${roomId}|${encodeUint32(fragmentId)}|${encodeUint32(eventIndex)}`; } -function encodeEventIdKey(roomId, eventId) { +function encodeEventIdKey(roomId: string, eventId: string): string { return `${roomId}|${eventId}`; } -function decodeEventIdKey(eventIdKey) { +function decodeEventIdKey(eventIdKey: string): { roomId: string, eventId: string } { const [roomId, eventId] = eventIdKey.split("|"); return {roomId, eventId}; } class Range { - constructor(IDBKeyRange, only, lower, upper, lowerOpen, upperOpen) { - this._IDBKeyRange = IDBKeyRange; + private _IDBKeyRange: typeof IDBKeyRange; + private _only?: EventKey; + private _lower?: EventKey; + private _upper?: EventKey; + private _lowerOpen: boolean; + private _upperOpen: boolean; + + constructor(_IDBKeyRange: any, only?: EventKey, lower?: EventKey, upper?: EventKey, lowerOpen: boolean = false, upperOpen: boolean = false) { + this._IDBKeyRange = _IDBKeyRange; this._only = only; this._lower = lower; this._upper = upper; @@ -42,7 +69,7 @@ class Range { this._upperOpen = upperOpen; } - asIDBKeyRange(roomId) { + asIDBKeyRange(roomId: string): IDBKeyRange | undefined { try { // only if (this._only) { @@ -99,66 +126,68 @@ class Range { * @property {?Gap} gap if a gap entry, the gap */ export class TimelineEventStore { - constructor(timelineStore) { + private _timelineStore: Store; + + constructor(timelineStore: Store) { this._timelineStore = timelineStore; } /** Creates a range that only includes the given key - * @param {EventKey} eventKey the key - * @return {Range} the created range + * @param eventKey the key + * @return the created range */ - onlyRange(eventKey) { + onlyRange(eventKey: EventKey): Range { return new Range(this._timelineStore.IDBKeyRange, eventKey); } /** Creates a range that includes all keys before eventKey, and optionally also the key itself. - * @param {EventKey} eventKey the key - * @param {boolean} [open=false] whether the key is included (false) or excluded (true) from the range at the upper end. - * @return {Range} the created range + * @param eventKey the key + * @param [open=false] whether the key is included (false) or excluded (true) from the range at the upper end. + * @return the created range */ - upperBoundRange(eventKey, open=false) { + upperBoundRange(eventKey: EventKey, open=false): Range { return new Range(this._timelineStore.IDBKeyRange, undefined, undefined, eventKey, undefined, open); } /** Creates a range that includes all keys after eventKey, and optionally also the key itself. - * @param {EventKey} eventKey the key - * @param {boolean} [open=false] whether the key is included (false) or excluded (true) from the range at the lower end. - * @return {Range} the created range + * @param eventKey the key + * @param [open=false] whether the key is included (false) or excluded (true) from the range at the lower end. + * @return the created range */ - lowerBoundRange(eventKey, open=false) { + lowerBoundRange(eventKey: EventKey, open=false): Range { return new Range(this._timelineStore.IDBKeyRange, undefined, eventKey, undefined, open); } /** Creates a range that includes all keys between `lower` and `upper`, and optionally the given keys as well. - * @param {EventKey} lower the lower key - * @param {EventKey} upper the upper key - * @param {boolean} [lowerOpen=false] whether the lower key is included (false) or excluded (true) from the range. - * @param {boolean} [upperOpen=false] whether the upper key is included (false) or excluded (true) from the range. - * @return {Range} the created range + * @param lower the lower key + * @param upper the upper key + * @param [lowerOpen=false] whether the lower key is included (false) or excluded (true) from the range. + * @param [upperOpen=false] whether the upper key is included (false) or excluded (true) from the range. + * @return the created range */ - boundRange(lower, upper, lowerOpen=false, upperOpen=false) { + boundRange(lower: EventKey, upper: EventKey, lowerOpen=false, upperOpen=false): Range { return new Range(this._timelineStore.IDBKeyRange, undefined, lower, upper, lowerOpen, upperOpen); } /** Looks up the last `amount` entries in the timeline for `roomId`. - * @param {string} roomId - * @param {number} fragmentId - * @param {number} amount - * @return {Promise} a promise resolving to an array with 0 or more entries, in ascending order. + * @param roomId + * @param fragmentId + * @param amount + * @return a promise resolving to an array with 0 or more entries, in ascending order. */ - async lastEvents(roomId, fragmentId, amount) { + async lastEvents(roomId: string, fragmentId: number, amount: number): Promise { const eventKey = EventKey.maxKey; eventKey.fragmentId = fragmentId; return this.eventsBefore(roomId, eventKey, amount); } /** Looks up the first `amount` entries in the timeline for `roomId`. - * @param {string} roomId - * @param {number} fragmentId - * @param {number} amount - * @return {Promise} a promise resolving to an array with 0 or more entries, in ascending order. + * @param roomId + * @param fragmentId + * @param amount + * @return a promise resolving to an array with 0 or more entries, in ascending order. */ - async firstEvents(roomId, fragmentId, amount) { + async firstEvents(roomId: string, fragmentId: number, amount: number): Promise { const eventKey = EventKey.minKey; eventKey.fragmentId = fragmentId; return this.eventsAfter(roomId, eventKey, amount); @@ -166,24 +195,24 @@ export class TimelineEventStore { /** Looks up `amount` entries after `eventKey` in the timeline for `roomId` within the same fragment. * The entry for `eventKey` is not included. - * @param {string} roomId - * @param {EventKey} eventKey - * @param {number} amount - * @return {Promise} a promise resolving to an array with 0 or more entries, in ascending order. + * @param roomId + * @param eventKey + * @param amount + * @return a promise resolving to an array with 0 or more entries, in ascending order. */ - eventsAfter(roomId, eventKey, amount) { + eventsAfter(roomId: string, eventKey: EventKey, amount: number): Promise { const idbRange = this.lowerBoundRange(eventKey, true).asIDBKeyRange(roomId); return this._timelineStore.selectLimit(idbRange, amount); } /** Looks up `amount` entries before `eventKey` in the timeline for `roomId` within the same fragment. * The entry for `eventKey` is not included. - * @param {string} roomId - * @param {EventKey} eventKey - * @param {number} amount - * @return {Promise} a promise resolving to an array with 0 or more entries, in ascending order. + * @param roomId + * @param eventKey + * @param amount + * @return a promise resolving to an array with 0 or more entries, in ascending order. */ - async eventsBefore(roomId, eventKey, amount) { + async eventsBefore(roomId: string, eventKey: EventKey, amount: number): Promise { const range = this.upperBoundRange(eventKey, true).asIDBKeyRange(roomId); const events = await this._timelineStore.selectLimitReverse(range, amount); events.reverse(); // because we fetched them backwards @@ -195,23 +224,23 @@ export class TimelineEventStore { * * The order in which results are returned might be different than `eventIds`. * Call the return value to obtain the next {id, event} pair. - * @param {string} roomId - * @param {string[]} eventIds - * @return {Function} + * @param roomId + * @param eventIds + * @return */ // performance comment from above refers to the fact that there *might* // be a correlation between event_id sorting order and chronology. // In that case we could avoid running over all eventIds, as the reported order by findExistingKeys // would match the order of eventIds. That's why findLast is also passed as backwards to keysExist. // also passing them in chronological order makes sense as that's how we'll receive them almost always. - async findFirstOccurringEventId(roomId, eventIds) { + async findFirstOccurringEventId(roomId: string, eventIds: string[]): Promise { const byEventId = this._timelineStore.index("byEventId"); const keys = eventIds.map(eventId => encodeEventIdKey(roomId, eventId)); const results = new Array(keys.length); - let firstFoundKey; + let firstFoundKey: string | undefined; // find first result that is found and has no undefined results before it - function firstFoundAndPrecedingResolved() { + function firstFoundAndPrecedingResolved(): string | undefined { for(let i = 0; i < results.length; ++i) { if (results[i] === undefined) { return; @@ -222,7 +251,8 @@ export class TimelineEventStore { } await byEventId.findExistingKeys(keys, false, (key, found) => { - const index = keys.indexOf(key); + // T[].search(T, number), but we want T[].search(R, number), so cast + const index = (keys as IDBValidKey[]).indexOf(key); results[index] = found; firstFoundKey = firstFoundAndPrecedingResolved(); return !!firstFoundKey; @@ -231,11 +261,11 @@ export class TimelineEventStore { } /** Inserts a new entry into the store. The combination of roomId and eventKey should not exist yet, or an error is thrown. - * @param {Entry} entry the entry to insert - * @return {Promise<>} a promise resolving to undefined if the operation was successful, or a StorageError if not. + * @param entry the entry to insert + * @return nothing. To wait for the operation to finish, await the transaction it's part of. * @throws {StorageError} ... */ - insert(entry) { + insert(entry: StorageEntry): void { entry.key = encodeKey(entry.roomId, entry.fragmentId, entry.eventIndex); entry.eventIdKey = encodeEventIdKey(entry.roomId, entry.event.event_id); // TODO: map error? or in idb/store? @@ -244,22 +274,22 @@ export class TimelineEventStore { /** Updates the entry into the store with the given [roomId, eventKey] combination. * If not yet present, will insert. Might be slower than add. - * @param {Entry} entry the entry to update. - * @return {Promise<>} a promise resolving to undefined if the operation was successful, or a StorageError if not. + * @param entry the entry to update. + * @return nothing. To wait for the operation to finish, await the transaction it's part of. */ - update(entry) { + update(entry: StorageEntry): void { this._timelineStore.put(entry); } - get(roomId, eventKey) { + get(roomId: string, eventKey: EventKey): Promise { return this._timelineStore.get(encodeKey(roomId, eventKey.fragmentId, eventKey.eventIndex)); } - getByEventId(roomId, eventId) { + getByEventId(roomId: string, eventId: string): Promise { return this._timelineStore.index("byEventId").get(encodeEventIdKey(roomId, eventId)); } - removeAllForRoom(roomId) { + removeAllForRoom(roomId: string): void { const minKey = encodeKey(roomId, KeyLimits.minStorageKey, KeyLimits.minStorageKey); const maxKey = encodeKey(roomId, KeyLimits.maxStorageKey, KeyLimits.maxStorageKey); const range = this._timelineStore.IDBKeyRange.bound(minKey, maxKey); diff --git a/src/matrix/storage/idb/stores/TimelineRelationStore.js b/src/matrix/storage/idb/stores/TimelineRelationStore.ts similarity index 68% rename from src/matrix/storage/idb/stores/TimelineRelationStore.js rename to src/matrix/storage/idb/stores/TimelineRelationStore.ts index bba24fc3..6772864a 100644 --- a/src/matrix/storage/idb/stores/TimelineRelationStore.js +++ b/src/matrix/storage/idb/stores/TimelineRelationStore.ts @@ -13,31 +13,41 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import {MIN_UNICODE, MAX_UNICODE} from "./common.js"; +import {MIN_UNICODE, MAX_UNICODE} from "./common"; +import {Store} from "../Store"; -function encodeKey(roomId, targetEventId, relType, sourceEventId) { +function encodeKey(roomId: string, targetEventId: string, relType: string, sourceEventId: string): string { return `${roomId}|${targetEventId}|${relType}|${sourceEventId}`; } -function decodeKey(key) { +interface RelationEntry { + roomId: string; + targetEventId: string; + sourceEventId: string; + relType: string; +} + +function decodeKey(key: string): RelationEntry { const [roomId, targetEventId, relType, sourceEventId] = key.split("|"); return {roomId, targetEventId, relType, sourceEventId}; } export class TimelineRelationStore { - constructor(store) { + private _store: Store<{ key: string }>; + + constructor(store: Store<{ key: string }>) { this._store = store; } - add(roomId, targetEventId, relType, sourceEventId) { - return this._store.add({key: encodeKey(roomId, targetEventId, relType, sourceEventId)}); + add(roomId: string, targetEventId: string, relType: string, sourceEventId: string): void { + this._store.add({key: encodeKey(roomId, targetEventId, relType, sourceEventId)}); } - remove(roomId, targetEventId, relType, sourceEventId) { + remove(roomId: string, targetEventId: string, relType: string, sourceEventId: string): Promise { return this._store.delete(encodeKey(roomId, targetEventId, relType, sourceEventId)); } - removeAllForTarget(roomId, targetId) { + removeAllForTarget(roomId: string, targetId: string): Promise { const range = this._store.IDBKeyRange.bound( encodeKey(roomId, targetId, MIN_UNICODE, MIN_UNICODE), encodeKey(roomId, targetId, MAX_UNICODE, MAX_UNICODE), @@ -47,7 +57,7 @@ export class TimelineRelationStore { return this._store.delete(range); } - async getForTargetAndType(roomId, targetId, relType) { + async getForTargetAndType(roomId: string, targetId: string, relType: string): Promise { // exclude both keys as they are theoretical min and max, // but we should't have a match for just the room id, or room id with max const range = this._store.IDBKeyRange.bound( @@ -60,7 +70,7 @@ export class TimelineRelationStore { return items.map(i => decodeKey(i.key)); } - async getAllForTarget(roomId, targetId) { + async getAllForTarget(roomId: string, targetId: string): Promise { // exclude both keys as they are theoretical min and max, // but we should't have a match for just the room id, or room id with max const range = this._store.IDBKeyRange.bound( diff --git a/src/matrix/storage/idb/stores/common.js b/src/matrix/storage/idb/stores/common.ts similarity index 100% rename from src/matrix/storage/idb/stores/common.js rename to src/matrix/storage/idb/stores/common.ts diff --git a/src/matrix/storage/idb/stores/InviteStore.js b/src/matrix/storage/types.ts similarity index 65% rename from src/matrix/storage/idb/stores/InviteStore.js rename to src/matrix/storage/types.ts index b0eefe60..a03fa1d7 100644 --- a/src/matrix/storage/idb/stores/InviteStore.js +++ b/src/matrix/storage/types.ts @@ -14,20 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -export class InviteStore { - constructor(inviteStore) { - this._inviteStore = inviteStore; - } +export type Content = { [key: string]: any } - getAll() { - return this._inviteStore.selectAll(); - } - - set(invite) { - return this._inviteStore.put(invite); - } - - remove(roomId) { - this._inviteStore.delete(roomId); - } +export interface TimelineEvent { + content: Content; + type: string; + event_id: string; + sender: string; + origin_server_ts: number; + unsigned?: Content; } + +export type StateEvent = TimelineEvent & { prev_content?: Content, state_key: string }