forked from mystiq/hydrogen-web
Merge pull request #474 from vector-im/snowpack-ts-storage-3
Snowpack + Typescript conversion (Part 3)
This commit is contained in:
commit
360b4db17a
15 changed files with 274 additions and 134 deletions
|
@ -22,18 +22,18 @@ 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 {RoomStateStore} from "./stores/RoomStateStore";
|
||||
import {RoomMemberStore} from "./stores/RoomMemberStore";
|
||||
import {TimelineFragmentStore} from "./stores/TimelineFragmentStore.js";
|
||||
import {PendingEventStore} from "./stores/PendingEventStore.js";
|
||||
import {UserIdentityStore} from "./stores/UserIdentityStore.js";
|
||||
import {DeviceIdentityStore} from "./stores/DeviceIdentityStore.js";
|
||||
import {OlmSessionStore} from "./stores/OlmSessionStore.js";
|
||||
import {InboundGroupSessionStore} from "./stores/InboundGroupSessionStore.js";
|
||||
import {OutboundGroupSessionStore} from "./stores/OutboundGroupSessionStore.js";
|
||||
import {GroupSessionDecryptionStore} from "./stores/GroupSessionDecryptionStore.js";
|
||||
import {OperationStore} from "./stores/OperationStore.js";
|
||||
import {AccountDataStore} from "./stores/AccountDataStore.js";
|
||||
import {TimelineFragmentStore} from "./stores/TimelineFragmentStore";
|
||||
import {PendingEventStore} from "./stores/PendingEventStore";
|
||||
import {UserIdentityStore} from "./stores/UserIdentityStore";
|
||||
import {DeviceIdentityStore} from "./stores/DeviceIdentityStore";
|
||||
import {OlmSessionStore} from "./stores/OlmSessionStore";
|
||||
import {InboundGroupSessionStore} from "./stores/InboundGroupSessionStore";
|
||||
import {OutboundGroupSessionStore} from "./stores/OutboundGroupSessionStore";
|
||||
import {GroupSessionDecryptionStore} from "./stores/GroupSessionDecryptionStore";
|
||||
import {OperationStore} from "./stores/OperationStore";
|
||||
import {AccountDataStore} from "./stores/AccountDataStore";
|
||||
|
||||
export class Transaction {
|
||||
constructor(txn, allowedStoreNames, IDBKeyRange) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/Ro
|
|||
import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js";
|
||||
import {RoomMemberStore} from "./stores/RoomMemberStore";
|
||||
import {SessionStore} from "./stores/SessionStore";
|
||||
import {encodeScopeTypeKey} from "./stores/OperationStore.js";
|
||||
import {encodeScopeTypeKey} from "./stores/OperationStore";
|
||||
import {MAX_UNICODE} from "./stores/common";
|
||||
|
||||
// FUNCTIONS SHOULD ONLY BE APPENDED!!
|
||||
|
|
|
@ -13,17 +13,26 @@ 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 {Content} from "../../types";
|
||||
|
||||
interface AccountDataEntry {
|
||||
type: string;
|
||||
content: Content;
|
||||
}
|
||||
|
||||
export class AccountDataStore {
|
||||
constructor(store) {
|
||||
private _store: Store<AccountDataEntry>;
|
||||
|
||||
constructor(store: Store<AccountDataEntry>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
async get(type) {
|
||||
async get(type: string): Promise<AccountDataEntry | null> {
|
||||
return await this._store.get(type);
|
||||
}
|
||||
|
||||
set(event) {
|
||||
return this._store.put(event);
|
||||
set(event: AccountDataEntry): void {
|
||||
this._store.put(event);
|
||||
}
|
||||
}
|
|
@ -15,33 +15,46 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {MAX_UNICODE, MIN_UNICODE} from "./common";
|
||||
import {Store} from "../Store";
|
||||
|
||||
function encodeKey(userId, deviceId) {
|
||||
interface DeviceIdentity {
|
||||
userId: string;
|
||||
deviceId: string;
|
||||
ed25519Key: string;
|
||||
curve25519Key: string;
|
||||
algorithms: string[];
|
||||
displayName: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
function encodeKey(userId: string, deviceId: string): string {
|
||||
return `${userId}|${deviceId}`;
|
||||
}
|
||||
|
||||
function decodeKey(key) {
|
||||
function decodeKey(key: string): { userId: string, deviceId: string } {
|
||||
const [userId, deviceId] = key.split("|");
|
||||
return {userId, deviceId};
|
||||
}
|
||||
|
||||
export class DeviceIdentityStore {
|
||||
constructor(store) {
|
||||
private _store: Store<DeviceIdentity>;
|
||||
|
||||
constructor(store: Store<DeviceIdentity>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
getAllForUserId(userId) {
|
||||
getAllForUserId(userId: string): Promise<DeviceIdentity[]> {
|
||||
const range = this._store.IDBKeyRange.lowerBound(encodeKey(userId, ""));
|
||||
return this._store.selectWhile(range, device => {
|
||||
return device.userId === userId;
|
||||
});
|
||||
}
|
||||
|
||||
async getAllDeviceIds(userId) {
|
||||
const deviceIds = [];
|
||||
async getAllDeviceIds(userId: string): Promise<string[]> {
|
||||
const deviceIds: string[] = [];
|
||||
const range = this._store.IDBKeyRange.lowerBound(encodeKey(userId, ""));
|
||||
await this._store.iterateKeys(range, key => {
|
||||
const decodedKey = decodeKey(key);
|
||||
const decodedKey = decodeKey(key as string);
|
||||
// prevent running into the next room
|
||||
if (decodedKey.userId === userId) {
|
||||
deviceIds.push(decodedKey.deviceId);
|
||||
|
@ -52,27 +65,27 @@ export class DeviceIdentityStore {
|
|||
return deviceIds;
|
||||
}
|
||||
|
||||
get(userId, deviceId) {
|
||||
get(userId: string, deviceId: string): Promise<DeviceIdentity | null> {
|
||||
return this._store.get(encodeKey(userId, deviceId));
|
||||
}
|
||||
|
||||
set(deviceIdentity) {
|
||||
set(deviceIdentity: DeviceIdentity): void {
|
||||
deviceIdentity.key = encodeKey(deviceIdentity.userId, deviceIdentity.deviceId);
|
||||
this._store.put(deviceIdentity);
|
||||
}
|
||||
|
||||
getByCurve25519Key(curve25519Key) {
|
||||
getByCurve25519Key(curve25519Key: string): Promise<DeviceIdentity | null> {
|
||||
return this._store.index("byCurve25519Key").get(curve25519Key);
|
||||
}
|
||||
|
||||
remove(userId, deviceId) {
|
||||
this._store.delete(encodeKey(userId, deviceId));
|
||||
remove(userId: string, deviceId: string): Promise<undefined> {
|
||||
return this._store.delete(encodeKey(userId, deviceId));
|
||||
}
|
||||
|
||||
removeAllForUser(userId) {
|
||||
removeAllForUser(userId: string): Promise<undefined> {
|
||||
// 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(encodeKey(userId, MIN_UNICODE), encodeKey(userId, MAX_UNICODE), true, true);
|
||||
this._store.delete(range);
|
||||
return this._store.delete(range);
|
||||
}
|
||||
}
|
|
@ -15,30 +15,40 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {MIN_UNICODE, MAX_UNICODE} from "./common";
|
||||
import {Store} from "../Store";
|
||||
|
||||
function encodeKey(roomId, sessionId, messageIndex) {
|
||||
function encodeKey(roomId: string, sessionId: string, messageIndex: number | string): string {
|
||||
return `${roomId}|${sessionId}|${messageIndex}`;
|
||||
}
|
||||
|
||||
interface GroupSessionDecryption {
|
||||
eventId: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
type GroupSessionEntry = GroupSessionDecryption & { key: string }
|
||||
|
||||
export class GroupSessionDecryptionStore {
|
||||
constructor(store) {
|
||||
private _store: Store<GroupSessionEntry>;
|
||||
|
||||
constructor(store: Store<GroupSessionEntry>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
get(roomId, sessionId, messageIndex) {
|
||||
get(roomId: string, sessionId: string, messageIndex: number): Promise<GroupSessionDecryption | null> {
|
||||
return this._store.get(encodeKey(roomId, sessionId, messageIndex));
|
||||
}
|
||||
|
||||
set(roomId, sessionId, messageIndex, decryption) {
|
||||
decryption.key = encodeKey(roomId, sessionId, messageIndex);
|
||||
this._store.put(decryption);
|
||||
set(roomId: string, sessionId: string, messageIndex: number, decryption: GroupSessionDecryption): void {
|
||||
(decryption as GroupSessionEntry).key = encodeKey(roomId, sessionId, messageIndex);
|
||||
this._store.put(decryption as GroupSessionEntry);
|
||||
}
|
||||
|
||||
removeAllForRoom(roomId) {
|
||||
removeAllForRoom(roomId: string): Promise<undefined> {
|
||||
const range = this._store.IDBKeyRange.bound(
|
||||
encodeKey(roomId, MIN_UNICODE, MIN_UNICODE),
|
||||
encodeKey(roomId, MAX_UNICODE, MAX_UNICODE)
|
||||
);
|
||||
this._store.delete(range);
|
||||
return this._store.delete(range);
|
||||
}
|
||||
}
|
|
@ -15,36 +15,49 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {MIN_UNICODE, MAX_UNICODE} from "./common";
|
||||
import {Store} from "../Store";
|
||||
|
||||
function encodeKey(roomId, senderKey, sessionId) {
|
||||
interface InboundGroupSession {
|
||||
roomId: string;
|
||||
senderKey: string;
|
||||
sessionId: string;
|
||||
session?: string;
|
||||
claimedKeys?: { [algorithm : string] : string };
|
||||
eventIds?: string[];
|
||||
key: string;
|
||||
}
|
||||
|
||||
function encodeKey(roomId: string, senderKey: string, sessionId: string): string {
|
||||
return `${roomId}|${senderKey}|${sessionId}`;
|
||||
}
|
||||
|
||||
export class InboundGroupSessionStore {
|
||||
constructor(store) {
|
||||
private _store: Store<InboundGroupSession>;
|
||||
|
||||
constructor(store: Store<InboundGroupSession>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
async has(roomId, senderKey, sessionId) {
|
||||
async has(roomId: string, senderKey: string, sessionId: string): Promise<boolean> {
|
||||
const key = encodeKey(roomId, senderKey, sessionId);
|
||||
const fetchedKey = await this._store.getKey(key);
|
||||
return key === fetchedKey;
|
||||
}
|
||||
|
||||
get(roomId, senderKey, sessionId) {
|
||||
get(roomId: string, senderKey: string, sessionId: string): Promise<InboundGroupSession | null> {
|
||||
return this._store.get(encodeKey(roomId, senderKey, sessionId));
|
||||
}
|
||||
|
||||
set(session) {
|
||||
set(session: InboundGroupSession): void {
|
||||
session.key = encodeKey(session.roomId, session.senderKey, session.sessionId);
|
||||
this._store.put(session);
|
||||
}
|
||||
|
||||
removeAllForRoom(roomId) {
|
||||
removeAllForRoom(roomId: string): Promise<undefined> {
|
||||
const range = this._store.IDBKeyRange.bound(
|
||||
encodeKey(roomId, MIN_UNICODE, MIN_UNICODE),
|
||||
encodeKey(roomId, MAX_UNICODE, MAX_UNICODE)
|
||||
);
|
||||
this._store.delete(range);
|
||||
return this._store.delete(range);
|
||||
}
|
||||
}
|
|
@ -13,26 +13,38 @@ 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";
|
||||
|
||||
function encodeKey(senderKey, sessionId) {
|
||||
function encodeKey(senderKey: string, sessionId: string): string {
|
||||
return `${senderKey}|${sessionId}`;
|
||||
}
|
||||
|
||||
function decodeKey(key) {
|
||||
function decodeKey(key: string): { senderKey: string, sessionId: string } {
|
||||
const [senderKey, sessionId] = key.split("|");
|
||||
return {senderKey, sessionId};
|
||||
}
|
||||
|
||||
interface OlmSession {
|
||||
session: string;
|
||||
sessionId: string;
|
||||
senderKey: string;
|
||||
lastUsed: number;
|
||||
}
|
||||
|
||||
type OlmSessionEntry = OlmSession & { key: string };
|
||||
|
||||
export class OlmSessionStore {
|
||||
constructor(store) {
|
||||
private _store: Store<OlmSessionEntry>;
|
||||
|
||||
constructor(store: Store<OlmSessionEntry>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
async getSessionIds(senderKey) {
|
||||
const sessionIds = [];
|
||||
async getSessionIds(senderKey: string): Promise<string[]> {
|
||||
const sessionIds: string[] = [];
|
||||
const range = this._store.IDBKeyRange.lowerBound(encodeKey(senderKey, ""));
|
||||
await this._store.iterateKeys(range, key => {
|
||||
const decodedKey = decodeKey(key);
|
||||
const decodedKey = decodeKey(key as string);
|
||||
// prevent running into the next room
|
||||
if (decodedKey.senderKey === senderKey) {
|
||||
sessionIds.push(decodedKey.sessionId);
|
||||
|
@ -43,23 +55,23 @@ export class OlmSessionStore {
|
|||
return sessionIds;
|
||||
}
|
||||
|
||||
getAll(senderKey) {
|
||||
getAll(senderKey: string): Promise<OlmSession[]> {
|
||||
const range = this._store.IDBKeyRange.lowerBound(encodeKey(senderKey, ""));
|
||||
return this._store.selectWhile(range, session => {
|
||||
return session.senderKey === senderKey;
|
||||
});
|
||||
}
|
||||
|
||||
get(senderKey, sessionId) {
|
||||
get(senderKey: string, sessionId: string): Promise<OlmSession | null> {
|
||||
return this._store.get(encodeKey(senderKey, sessionId));
|
||||
}
|
||||
|
||||
set(session) {
|
||||
session.key = encodeKey(session.senderKey, session.sessionId);
|
||||
return this._store.put(session);
|
||||
set(session: OlmSession): void {
|
||||
(session as OlmSessionEntry).key = encodeKey(session.senderKey, session.sessionId);
|
||||
this._store.put(session as OlmSessionEntry);
|
||||
}
|
||||
|
||||
remove(senderKey, sessionId) {
|
||||
remove(senderKey: string, sessionId: string): Promise<undefined> {
|
||||
return this._store.delete(encodeKey(senderKey, sessionId));
|
||||
}
|
||||
}
|
|
@ -14,23 +14,46 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
import {MIN_UNICODE, MAX_UNICODE} from "./common";
|
||||
import {Store} from "../Store";
|
||||
|
||||
export function encodeScopeTypeKey(scope, type) {
|
||||
export function encodeScopeTypeKey(scope: string, type: string): string {
|
||||
return `${scope}|${type}`;
|
||||
}
|
||||
|
||||
interface BaseOperation {
|
||||
id: string;
|
||||
scope: string;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
type OperationType = { type: "share_room_key"; roomKeyMessage: RoomKeyMessage; }
|
||||
|
||||
type Operation = BaseOperation & OperationType
|
||||
|
||||
type OperationEntry = Operation & { scopeTypeKey: string; }
|
||||
|
||||
interface RoomKeyMessage {
|
||||
room_id: string;
|
||||
session_id: string;
|
||||
session_key: string;
|
||||
algorithm: string;
|
||||
chain_index: number;
|
||||
}
|
||||
|
||||
export class OperationStore {
|
||||
constructor(store) {
|
||||
private _store: Store<OperationEntry>;
|
||||
|
||||
constructor(store: Store<OperationEntry>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
getAll(): Promise<Operation[]> {
|
||||
return this._store.selectAll();
|
||||
}
|
||||
|
||||
async getAllByTypeAndScope(type, scope) {
|
||||
async getAllByTypeAndScope(type: string, scope: string): Promise<Operation[]> {
|
||||
const key = encodeScopeTypeKey(scope, type);
|
||||
const results = [];
|
||||
const results: Operation[] = [];
|
||||
await this._store.index("byScopeAndType").iterateWhile(key, value => {
|
||||
if (value.scopeTypeKey !== key) {
|
||||
return false;
|
||||
|
@ -41,20 +64,20 @@ export class OperationStore {
|
|||
return results;
|
||||
}
|
||||
|
||||
add(operation) {
|
||||
operation.scopeTypeKey = encodeScopeTypeKey(operation.scope, operation.type);
|
||||
this._store.add(operation);
|
||||
add(operation: Operation): void {
|
||||
(operation as OperationEntry).scopeTypeKey = encodeScopeTypeKey(operation.scope, operation.type);
|
||||
this._store.add(operation as OperationEntry);
|
||||
}
|
||||
|
||||
update(operation) {
|
||||
this._store.put(operation);
|
||||
update(operation: Operation): void {
|
||||
this._store.put(operation as OperationEntry);
|
||||
}
|
||||
|
||||
remove(id) {
|
||||
this._store.delete(id);
|
||||
remove(id: string): Promise<undefined> {
|
||||
return this._store.delete(id);
|
||||
}
|
||||
|
||||
async removeAllForScope(scope) {
|
||||
async removeAllForScope(scope: string): Promise<undefined> {
|
||||
const range = this._store.IDBKeyRange.bound(
|
||||
encodeScopeTypeKey(scope, MIN_UNICODE),
|
||||
encodeScopeTypeKey(scope, MAX_UNICODE)
|
||||
|
@ -64,5 +87,6 @@ export class OperationStore {
|
|||
cur.delete();
|
||||
return true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -13,21 +13,30 @@ 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";
|
||||
|
||||
interface OutboundSession {
|
||||
roomId: string;
|
||||
session: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
export class OutboundGroupSessionStore {
|
||||
constructor(store) {
|
||||
private _store: Store<OutboundSession>;
|
||||
|
||||
constructor(store: Store<OutboundSession>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
remove(roomId) {
|
||||
this._store.delete(roomId);
|
||||
remove(roomId: string): Promise<undefined> {
|
||||
return this._store.delete(roomId);
|
||||
}
|
||||
|
||||
get(roomId) {
|
||||
get(roomId: string): Promise<OutboundSession | null> {
|
||||
return this._store.get(roomId);
|
||||
}
|
||||
|
||||
set(session) {
|
||||
set(session: OutboundSession): void {
|
||||
this._store.put(session);
|
||||
}
|
||||
}
|
|
@ -16,23 +16,40 @@ limitations under the License.
|
|||
|
||||
import { encodeUint32, decodeUint32 } from "../utils";
|
||||
import {KeyLimits} from "../../common";
|
||||
import {Store} from "../Store";
|
||||
import {Content} from "../../types";
|
||||
|
||||
function encodeKey(roomId, queueIndex) {
|
||||
interface PendingEntry {
|
||||
roomId: string;
|
||||
queueIndex: number;
|
||||
eventType: string;
|
||||
content: Content;
|
||||
relatexTxnId: string | null;
|
||||
relatedEventId: string | null;
|
||||
txnId?: string;
|
||||
needsEncryption: boolean;
|
||||
needsUpload: boolean;
|
||||
key: string;
|
||||
}
|
||||
|
||||
function encodeKey(roomId: string, queueIndex: number): string {
|
||||
return `${roomId}|${encodeUint32(queueIndex)}`;
|
||||
}
|
||||
|
||||
function decodeKey(key) {
|
||||
function decodeKey(key: string): { roomId: string, queueIndex: number } {
|
||||
const [roomId, encodedQueueIndex] = key.split("|");
|
||||
const queueIndex = decodeUint32(encodedQueueIndex);
|
||||
return {roomId, queueIndex};
|
||||
}
|
||||
|
||||
export class PendingEventStore {
|
||||
constructor(eventStore) {
|
||||
private _eventStore: Store<PendingEntry>;
|
||||
|
||||
constructor(eventStore: Store<PendingEntry>) {
|
||||
this._eventStore = eventStore;
|
||||
}
|
||||
|
||||
async getMaxQueueIndex(roomId) {
|
||||
async getMaxQueueIndex(roomId: string): Promise<number | undefined> {
|
||||
const range = this._eventStore.IDBKeyRange.bound(
|
||||
encodeKey(roomId, KeyLimits.minStorageKey),
|
||||
encodeKey(roomId, KeyLimits.maxStorageKey),
|
||||
|
@ -41,38 +58,38 @@ export class PendingEventStore {
|
|||
);
|
||||
const maxKey = await this._eventStore.findMaxKey(range);
|
||||
if (maxKey) {
|
||||
return decodeKey(maxKey).queueIndex;
|
||||
return decodeKey(maxKey as string).queueIndex;
|
||||
}
|
||||
}
|
||||
|
||||
remove(roomId, queueIndex) {
|
||||
remove(roomId: string, queueIndex: number): Promise<undefined> {
|
||||
const keyRange = this._eventStore.IDBKeyRange.only(encodeKey(roomId, queueIndex));
|
||||
this._eventStore.delete(keyRange);
|
||||
return this._eventStore.delete(keyRange);
|
||||
}
|
||||
|
||||
async exists(roomId, queueIndex) {
|
||||
async exists(roomId: string, queueIndex: number): Promise<boolean> {
|
||||
const keyRange = this._eventStore.IDBKeyRange.only(encodeKey(roomId, queueIndex));
|
||||
const key = await this._eventStore.getKey(keyRange);
|
||||
return !!key;
|
||||
}
|
||||
|
||||
add(pendingEvent) {
|
||||
add(pendingEvent: PendingEntry): void {
|
||||
pendingEvent.key = encodeKey(pendingEvent.roomId, pendingEvent.queueIndex);
|
||||
this._eventStore.add(pendingEvent);
|
||||
}
|
||||
|
||||
update(pendingEvent) {
|
||||
update(pendingEvent: PendingEntry): void {
|
||||
this._eventStore.put(pendingEvent);
|
||||
}
|
||||
|
||||
getAll() {
|
||||
getAll(): Promise<PendingEntry[]> {
|
||||
return this._eventStore.selectAll();
|
||||
}
|
||||
|
||||
removeAllForRoom(roomId) {
|
||||
removeAllForRoom(roomId: string): Promise<undefined> {
|
||||
const minKey = encodeKey(roomId, KeyLimits.minStorageKey);
|
||||
const maxKey = encodeKey(roomId, KeyLimits.maxStorageKey);
|
||||
const range = this._eventStore.IDBKeyRange.bound(minKey, maxKey);
|
||||
this._eventStore.delete(range);
|
||||
return this._eventStore.delete(range);
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ export class RoomMemberStore {
|
|||
|
||||
set(member: MemberData): void {
|
||||
// Object.assign would be more typesafe, but small objects
|
||||
(member as any).key = encodeKey(member.roomId, member.userId);
|
||||
(member as MemberStorageEntry).key = encodeKey(member.roomId, member.userId);
|
||||
this._roomMembersStore.put(member as MemberStorageEntry);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,31 +16,41 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {MAX_UNICODE} from "./common";
|
||||
import {Store} from "../Store";
|
||||
import {StateEvent} from "../../types";
|
||||
|
||||
function encodeKey(roomId, eventType, stateKey) {
|
||||
function encodeKey(roomId: string, eventType: string, stateKey: string) {
|
||||
return `${roomId}|${eventType}|${stateKey}`;
|
||||
}
|
||||
|
||||
export interface RoomStateEntry {
|
||||
roomId: string;
|
||||
event: StateEvent;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export class RoomStateStore {
|
||||
constructor(idbStore) {
|
||||
private _roomStateStore: Store<RoomStateEntry>;
|
||||
|
||||
constructor(idbStore: Store<RoomStateEntry>) {
|
||||
this._roomStateStore = idbStore;
|
||||
}
|
||||
|
||||
get(roomId, type, stateKey) {
|
||||
get(roomId: string, type: string, stateKey: string): Promise<RoomStateEntry | null> {
|
||||
const key = encodeKey(roomId, type, stateKey);
|
||||
return this._roomStateStore.get(key);
|
||||
}
|
||||
|
||||
set(roomId, event) {
|
||||
set(roomId: string, event: StateEvent): void {
|
||||
const key = encodeKey(roomId, event.type, event.state_key);
|
||||
const entry = {roomId, event, key};
|
||||
return this._roomStateStore.put(entry);
|
||||
this._roomStateStore.put(entry);
|
||||
}
|
||||
|
||||
removeAllForRoom(roomId) {
|
||||
removeAllForRoom(roomId: string): Promise<undefined> {
|
||||
// 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._roomStateStore.IDBKeyRange.bound(roomId, `${roomId}|${MAX_UNICODE}`, true, true);
|
||||
this._roomStateStore.delete(range);
|
||||
return this._roomStateStore.delete(range);
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ interface Annotation {
|
|||
firstTimestamp: number;
|
||||
}
|
||||
|
||||
interface StorageEntry {
|
||||
interface TimelineEventEntry {
|
||||
roomId: string;
|
||||
fragmentId: number;
|
||||
eventIndex: number;
|
||||
|
@ -35,10 +35,10 @@ interface StorageEntry {
|
|||
displayName?: string;
|
||||
avatarUrl?: string;
|
||||
annotations?: { [key : string]: Annotation };
|
||||
key: string;
|
||||
eventIdKey: string;
|
||||
}
|
||||
|
||||
type TimelineEventStorageEntry = TimelineEventEntry & { key: string, eventIdKey: string };
|
||||
|
||||
function encodeKey(roomId: string, fragmentId: number, eventIndex: number): string {
|
||||
return `${roomId}|${encodeUint32(fragmentId)}|${encodeUint32(eventIndex)}`;
|
||||
}
|
||||
|
@ -126,9 +126,9 @@ class Range {
|
|||
* @property {?Gap} gap if a gap entry, the gap
|
||||
*/
|
||||
export class TimelineEventStore {
|
||||
private _timelineStore: Store<StorageEntry>;
|
||||
private _timelineStore: Store<TimelineEventStorageEntry>;
|
||||
|
||||
constructor(timelineStore: Store<StorageEntry>) {
|
||||
constructor(timelineStore: Store<TimelineEventStorageEntry>) {
|
||||
this._timelineStore = timelineStore;
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ export class TimelineEventStore {
|
|||
* @param amount
|
||||
* @return a promise resolving to an array with 0 or more entries, in ascending order.
|
||||
*/
|
||||
async lastEvents(roomId: string, fragmentId: number, amount: number): Promise<StorageEntry[]> {
|
||||
async lastEvents(roomId: string, fragmentId: number, amount: number): Promise<TimelineEventEntry[]> {
|
||||
const eventKey = EventKey.maxKey;
|
||||
eventKey.fragmentId = fragmentId;
|
||||
return this.eventsBefore(roomId, eventKey, amount);
|
||||
|
@ -187,7 +187,7 @@ export class TimelineEventStore {
|
|||
* @param amount
|
||||
* @return a promise resolving to an array with 0 or more entries, in ascending order.
|
||||
*/
|
||||
async firstEvents(roomId: string, fragmentId: number, amount: number): Promise<StorageEntry[]> {
|
||||
async firstEvents(roomId: string, fragmentId: number, amount: number): Promise<TimelineEventEntry[]> {
|
||||
const eventKey = EventKey.minKey;
|
||||
eventKey.fragmentId = fragmentId;
|
||||
return this.eventsAfter(roomId, eventKey, amount);
|
||||
|
@ -200,7 +200,7 @@ export class TimelineEventStore {
|
|||
* @param amount
|
||||
* @return a promise resolving to an array with 0 or more entries, in ascending order.
|
||||
*/
|
||||
eventsAfter(roomId: string, eventKey: EventKey, amount: number): Promise<StorageEntry[]> {
|
||||
eventsAfter(roomId: string, eventKey: EventKey, amount: number): Promise<TimelineEventEntry[]> {
|
||||
const idbRange = this.lowerBoundRange(eventKey, true).asIDBKeyRange(roomId);
|
||||
return this._timelineStore.selectLimit(idbRange, amount);
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ export class TimelineEventStore {
|
|||
* @param amount
|
||||
* @return a promise resolving to an array with 0 or more entries, in ascending order.
|
||||
*/
|
||||
async eventsBefore(roomId: string, eventKey: EventKey, amount: number): Promise<StorageEntry[]> {
|
||||
async eventsBefore(roomId: string, eventKey: EventKey, amount: number): Promise<TimelineEventEntry[]> {
|
||||
const range = this.upperBoundRange(eventKey, true).asIDBKeyRange(roomId);
|
||||
const events = await this._timelineStore.selectLimitReverse(range, amount);
|
||||
events.reverse(); // because we fetched them backwards
|
||||
|
@ -265,11 +265,11 @@ export class TimelineEventStore {
|
|||
* @return nothing. To wait for the operation to finish, await the transaction it's part of.
|
||||
* @throws {StorageError} ...
|
||||
*/
|
||||
insert(entry: StorageEntry): void {
|
||||
entry.key = encodeKey(entry.roomId, entry.fragmentId, entry.eventIndex);
|
||||
entry.eventIdKey = encodeEventIdKey(entry.roomId, entry.event.event_id);
|
||||
insert(entry: TimelineEventEntry): void {
|
||||
(entry as TimelineEventStorageEntry).key = encodeKey(entry.roomId, entry.fragmentId, entry.eventIndex);
|
||||
(entry as TimelineEventStorageEntry).eventIdKey = encodeEventIdKey(entry.roomId, entry.event.event_id);
|
||||
// TODO: map error? or in idb/store?
|
||||
this._timelineStore.add(entry);
|
||||
this._timelineStore.add(entry as TimelineEventStorageEntry);
|
||||
}
|
||||
|
||||
/** Updates the entry into the store with the given [roomId, eventKey] combination.
|
||||
|
@ -277,15 +277,15 @@ export class TimelineEventStore {
|
|||
* @param entry the entry to update.
|
||||
* @return nothing. To wait for the operation to finish, await the transaction it's part of.
|
||||
*/
|
||||
update(entry: StorageEntry): void {
|
||||
this._timelineStore.put(entry);
|
||||
update(entry: TimelineEventEntry): void {
|
||||
this._timelineStore.put(entry as TimelineEventStorageEntry);
|
||||
}
|
||||
|
||||
get(roomId: string, eventKey: EventKey): Promise<StorageEntry | null> {
|
||||
get(roomId: string, eventKey: EventKey): Promise<TimelineEventEntry | null> {
|
||||
return this._timelineStore.get(encodeKey(roomId, eventKey.fragmentId, eventKey.eventIndex));
|
||||
}
|
||||
|
||||
getByEventId(roomId: string, eventId: string): Promise<StorageEntry | null> {
|
||||
getByEventId(roomId: string, eventId: string): Promise<TimelineEventEntry | null> {
|
||||
return this._timelineStore.index("byEventId").get(encodeEventIdKey(roomId, eventId));
|
||||
}
|
||||
|
||||
|
|
|
@ -17,17 +17,31 @@ limitations under the License.
|
|||
import { StorageError } from "../../common";
|
||||
import {KeyLimits} from "../../common";
|
||||
import { encodeUint32 } from "../utils";
|
||||
import {Store} from "../Store";
|
||||
|
||||
function encodeKey(roomId, fragmentId) {
|
||||
interface Fragment {
|
||||
roomId: string;
|
||||
id: number;
|
||||
previousId: number | null;
|
||||
nextId: number | null;
|
||||
previousToken: string | null;
|
||||
nextToken: string | null;
|
||||
}
|
||||
|
||||
type FragmentEntry = Fragment & { key: string }
|
||||
|
||||
function encodeKey(roomId: string, fragmentId: number): string {
|
||||
return `${roomId}|${encodeUint32(fragmentId)}`;
|
||||
}
|
||||
|
||||
export class TimelineFragmentStore {
|
||||
constructor(store) {
|
||||
private _store: Store<FragmentEntry>;
|
||||
|
||||
constructor(store: Store<FragmentEntry>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
_allRange(roomId) {
|
||||
_allRange(roomId: string): IDBKeyRange {
|
||||
try {
|
||||
return this._store.IDBKeyRange.bound(
|
||||
encodeKey(roomId, KeyLimits.minStorageKey),
|
||||
|
@ -38,13 +52,13 @@ export class TimelineFragmentStore {
|
|||
}
|
||||
}
|
||||
|
||||
all(roomId) {
|
||||
all(roomId: string): Promise<FragmentEntry[]> {
|
||||
return this._store.selectAll(this._allRange(roomId));
|
||||
}
|
||||
|
||||
/** Returns the fragment without a nextToken and without nextId,
|
||||
if any, with the largest id if there are multiple (which should not happen) */
|
||||
liveFragment(roomId) {
|
||||
liveFragment(roomId: string): Promise<FragmentEntry | undefined> {
|
||||
// why do we need this?
|
||||
// Ok, take the case where you've got a /context fragment and a /sync fragment
|
||||
// They are not connected. So, upon loading the persister, which one do we take? We can't sort them ...
|
||||
|
@ -60,20 +74,20 @@ export class TimelineFragmentStore {
|
|||
// should generate an id an return it?
|
||||
// depends if we want to do anything smart with fragment ids,
|
||||
// like give them meaning depending on range. not for now probably ...
|
||||
add(fragment) {
|
||||
fragment.key = encodeKey(fragment.roomId, fragment.id);
|
||||
this._store.add(fragment);
|
||||
add(fragment: Fragment): void {
|
||||
(fragment as FragmentEntry).key = encodeKey(fragment.roomId, fragment.id);
|
||||
this._store.add(fragment as FragmentEntry);
|
||||
}
|
||||
|
||||
update(fragment) {
|
||||
update(fragment: FragmentEntry): void {
|
||||
this._store.put(fragment);
|
||||
}
|
||||
|
||||
get(roomId, fragmentId) {
|
||||
get(roomId: string, fragmentId: number): Promise<FragmentEntry | null> {
|
||||
return this._store.get(encodeKey(roomId, fragmentId));
|
||||
}
|
||||
|
||||
removeAllForRoom(roomId) {
|
||||
this._store.delete(this._allRange(roomId));
|
||||
removeAllForRoom(roomId: string): Promise<undefined> {
|
||||
return this._store.delete(this._allRange(roomId));
|
||||
}
|
||||
}
|
|
@ -13,21 +13,30 @@ 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";
|
||||
|
||||
interface UserIdentity {
|
||||
userId: string;
|
||||
roomIds: string[];
|
||||
deviceTrackingStatus: number;
|
||||
}
|
||||
|
||||
export class UserIdentityStore {
|
||||
constructor(store) {
|
||||
private _store: Store<UserIdentity>;
|
||||
|
||||
constructor(store: Store<UserIdentity>) {
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
get(userId) {
|
||||
get(userId: string): Promise<UserIdentity | null> {
|
||||
return this._store.get(userId);
|
||||
}
|
||||
|
||||
set(userIdentity) {
|
||||
set(userIdentity: UserIdentity): void {
|
||||
this._store.put(userIdentity);
|
||||
}
|
||||
|
||||
remove(userId) {
|
||||
remove(userId: string): Promise<undefined> {
|
||||
return this._store.delete(userId);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue