You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-09 10:22:46 +03:00
Remove unused sessionStore (#2455)
* Remove unused sessionStorage layer * Move pending event abstraction into its temporary home * Add test coverage * Tweak * Fix tests mocks * Add coverage * Add coverage
This commit is contained in:
committed by
GitHub
parent
eb8491c91b
commit
b9ca3ceacd
@@ -23,7 +23,6 @@ import MockHttpBackend from 'matrix-mock-request';
|
|||||||
|
|
||||||
import { LocalStorageCryptoStore } from '../src/crypto/store/localStorage-crypto-store';
|
import { LocalStorageCryptoStore } from '../src/crypto/store/localStorage-crypto-store';
|
||||||
import { logger } from '../src/logger';
|
import { logger } from '../src/logger';
|
||||||
import { WebStorageSessionStore } from "../src/store/session/webstorage";
|
|
||||||
import { syncPromise } from "./test-utils/test-utils";
|
import { syncPromise } from "./test-utils/test-utils";
|
||||||
import { createClient } from "../src/matrix";
|
import { createClient } from "../src/matrix";
|
||||||
import { ICreateClientOpts, IDownloadKeyResult, MatrixClient, PendingEventOrdering } from "../src/client";
|
import { ICreateClientOpts, IDownloadKeyResult, MatrixClient, PendingEventOrdering } from "../src/client";
|
||||||
@@ -53,7 +52,6 @@ export class TestClient {
|
|||||||
if (sessionStoreBackend === undefined) {
|
if (sessionStoreBackend === undefined) {
|
||||||
sessionStoreBackend = new MockStorageApi();
|
sessionStoreBackend = new MockStorageApi();
|
||||||
}
|
}
|
||||||
const sessionStore = new WebStorageSessionStore(sessionStoreBackend);
|
|
||||||
|
|
||||||
this.httpBackend = new MockHttpBackend();
|
this.httpBackend = new MockHttpBackend();
|
||||||
|
|
||||||
@@ -62,7 +60,6 @@ export class TestClient {
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
accessToken: accessToken,
|
accessToken: accessToken,
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
sessionStore: sessionStore,
|
|
||||||
request: this.httpBackend.requestFn as IHttpOpts["request"],
|
request: this.httpBackend.requestFn as IHttpOpts["request"],
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
|
@@ -3,7 +3,6 @@ import '../olm-loader';
|
|||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
import { Crypto } from "../../src/crypto";
|
import { Crypto } from "../../src/crypto";
|
||||||
import { WebStorageSessionStore } from "../../src/store/session/webstorage";
|
|
||||||
import { MemoryCryptoStore } from "../../src/crypto/store/memory-crypto-store";
|
import { MemoryCryptoStore } from "../../src/crypto/store/memory-crypto-store";
|
||||||
import { MockStorageApi } from "../MockStorageApi";
|
import { MockStorageApi } from "../MockStorageApi";
|
||||||
import { TestClient } from "../TestClient";
|
import { TestClient } from "../TestClient";
|
||||||
@@ -14,6 +13,7 @@ import { sleep } from "../../src/utils";
|
|||||||
import { CRYPTO_ENABLED } from "../../src/client";
|
import { CRYPTO_ENABLED } from "../../src/client";
|
||||||
import { DeviceInfo } from "../../src/crypto/deviceinfo";
|
import { DeviceInfo } from "../../src/crypto/deviceinfo";
|
||||||
import { logger } from '../../src/logger';
|
import { logger } from '../../src/logger';
|
||||||
|
import { MemoryStore } from "../../src";
|
||||||
|
|
||||||
const Olm = global.Olm;
|
const Olm = global.Olm;
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ describe("Crypto", function() {
|
|||||||
|
|
||||||
beforeEach(async function() {
|
beforeEach(async function() {
|
||||||
const mockStorage = new MockStorageApi();
|
const mockStorage = new MockStorageApi();
|
||||||
const sessionStore = new WebStorageSessionStore(mockStorage);
|
const clientStore = new MemoryStore({ localStorage: mockStorage });
|
||||||
const cryptoStore = new MemoryCryptoStore(mockStorage);
|
const cryptoStore = new MemoryCryptoStore(mockStorage);
|
||||||
|
|
||||||
cryptoStore.storeEndToEndDeviceData({
|
cryptoStore.storeEndToEndDeviceData({
|
||||||
@@ -180,10 +180,9 @@ describe("Crypto", function() {
|
|||||||
|
|
||||||
crypto = new Crypto(
|
crypto = new Crypto(
|
||||||
mockBaseApis,
|
mockBaseApis,
|
||||||
sessionStore,
|
|
||||||
"@alice:home.server",
|
"@alice:home.server",
|
||||||
"FLIBBLE",
|
"FLIBBLE",
|
||||||
sessionStore,
|
clientStore,
|
||||||
cryptoStore,
|
cryptoStore,
|
||||||
mockRoomList,
|
mockRoomList,
|
||||||
);
|
);
|
||||||
|
@@ -21,7 +21,6 @@ import * as olmlib from "../../../src/crypto/olmlib";
|
|||||||
import { MatrixClient } from "../../../src/client";
|
import { MatrixClient } from "../../../src/client";
|
||||||
import { MatrixEvent } from "../../../src/models/event";
|
import { MatrixEvent } from "../../../src/models/event";
|
||||||
import * as algorithms from "../../../src/crypto/algorithms";
|
import * as algorithms from "../../../src/crypto/algorithms";
|
||||||
import { WebStorageSessionStore } from "../../../src/store/session/webstorage";
|
|
||||||
import { MemoryCryptoStore } from "../../../src/crypto/store/memory-crypto-store";
|
import { MemoryCryptoStore } from "../../../src/crypto/store/memory-crypto-store";
|
||||||
import { MockStorageApi } from "../../MockStorageApi";
|
import { MockStorageApi } from "../../MockStorageApi";
|
||||||
import * as testUtils from "../../test-utils/test-utils";
|
import * as testUtils from "../../test-utils/test-utils";
|
||||||
@@ -118,7 +117,7 @@ function saveCrossSigningKeys(k) {
|
|||||||
Object.assign(keys, k);
|
Object.assign(keys, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTestClient(sessionStore, cryptoStore) {
|
function makeTestClient(cryptoStore) {
|
||||||
const scheduler = [
|
const scheduler = [
|
||||||
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
|
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
|
||||||
"setProcessFunction",
|
"setProcessFunction",
|
||||||
@@ -141,7 +140,6 @@ function makeTestClient(sessionStore, cryptoStore) {
|
|||||||
scheduler: scheduler,
|
scheduler: scheduler,
|
||||||
userId: "@alice:bar",
|
userId: "@alice:bar",
|
||||||
deviceId: "device",
|
deviceId: "device",
|
||||||
sessionStore: sessionStore,
|
|
||||||
cryptoStore: cryptoStore,
|
cryptoStore: cryptoStore,
|
||||||
cryptoCallbacks: { getCrossSigningKey, saveCrossSigningKeys },
|
cryptoCallbacks: { getCrossSigningKey, saveCrossSigningKeys },
|
||||||
});
|
});
|
||||||
@@ -161,7 +159,6 @@ describe("MegolmBackup", function() {
|
|||||||
let mockOlmLib;
|
let mockOlmLib;
|
||||||
let mockCrypto;
|
let mockCrypto;
|
||||||
let mockStorage;
|
let mockStorage;
|
||||||
let sessionStore;
|
|
||||||
let cryptoStore;
|
let cryptoStore;
|
||||||
let megolmDecryption;
|
let megolmDecryption;
|
||||||
beforeEach(async function() {
|
beforeEach(async function() {
|
||||||
@@ -174,7 +171,6 @@ describe("MegolmBackup", function() {
|
|||||||
mockCrypto.backupInfo = CURVE25519_BACKUP_INFO;
|
mockCrypto.backupInfo = CURVE25519_BACKUP_INFO;
|
||||||
|
|
||||||
mockStorage = new MockStorageApi();
|
mockStorage = new MockStorageApi();
|
||||||
sessionStore = new WebStorageSessionStore(mockStorage);
|
|
||||||
cryptoStore = new MemoryCryptoStore(mockStorage);
|
cryptoStore = new MemoryCryptoStore(mockStorage);
|
||||||
|
|
||||||
olmDevice = new OlmDevice(cryptoStore);
|
olmDevice = new OlmDevice(cryptoStore);
|
||||||
@@ -261,7 +257,7 @@ describe("MegolmBackup", function() {
|
|||||||
const ibGroupSession = new Olm.InboundGroupSession();
|
const ibGroupSession = new Olm.InboundGroupSession();
|
||||||
ibGroupSession.create(groupSession.session_key());
|
ibGroupSession.create(groupSession.session_key());
|
||||||
|
|
||||||
const client = makeTestClient(sessionStore, cryptoStore);
|
const client = makeTestClient(cryptoStore);
|
||||||
|
|
||||||
megolmDecryption = new MegolmDecryption({
|
megolmDecryption = new MegolmDecryption({
|
||||||
userId: '@user:id',
|
userId: '@user:id',
|
||||||
@@ -340,7 +336,7 @@ describe("MegolmBackup", function() {
|
|||||||
const ibGroupSession = new Olm.InboundGroupSession();
|
const ibGroupSession = new Olm.InboundGroupSession();
|
||||||
ibGroupSession.create(groupSession.session_key());
|
ibGroupSession.create(groupSession.session_key());
|
||||||
|
|
||||||
const client = makeTestClient(sessionStore, cryptoStore);
|
const client = makeTestClient(cryptoStore);
|
||||||
|
|
||||||
megolmDecryption = new MegolmDecryption({
|
megolmDecryption = new MegolmDecryption({
|
||||||
userId: '@user:id',
|
userId: '@user:id',
|
||||||
@@ -423,7 +419,7 @@ describe("MegolmBackup", function() {
|
|||||||
const ibGroupSession = new Olm.InboundGroupSession();
|
const ibGroupSession = new Olm.InboundGroupSession();
|
||||||
ibGroupSession.create(groupSession.session_key());
|
ibGroupSession.create(groupSession.session_key());
|
||||||
|
|
||||||
const client = makeTestClient(sessionStore, cryptoStore);
|
const client = makeTestClient(cryptoStore);
|
||||||
|
|
||||||
megolmDecryption = new MegolmDecryption({
|
megolmDecryption = new MegolmDecryption({
|
||||||
userId: '@user:id',
|
userId: '@user:id',
|
||||||
@@ -520,7 +516,6 @@ describe("MegolmBackup", function() {
|
|||||||
scheduler: scheduler,
|
scheduler: scheduler,
|
||||||
userId: "@alice:bar",
|
userId: "@alice:bar",
|
||||||
deviceId: "device",
|
deviceId: "device",
|
||||||
sessionStore: sessionStore,
|
|
||||||
cryptoStore: cryptoStore,
|
cryptoStore: cryptoStore,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -606,7 +601,7 @@ describe("MegolmBackup", function() {
|
|||||||
let client;
|
let client;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
client = makeTestClient(sessionStore, cryptoStore);
|
client = makeTestClient(cryptoStore);
|
||||||
|
|
||||||
megolmDecryption = new MegolmDecryption({
|
megolmDecryption = new MegolmDecryption({
|
||||||
userId: '@user:id',
|
userId: '@user:id',
|
||||||
|
@@ -1555,6 +1555,8 @@ describe("Room", function() {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
getSyncToken: () => "sync_token",
|
getSyncToken: () => "sync_token",
|
||||||
|
getPendingEvents: jest.fn().mockResolvedValue([]),
|
||||||
|
setPendingEvents: jest.fn().mockResolvedValue(undefined),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -17,13 +17,17 @@ limitations under the License.
|
|||||||
import 'fake-indexeddb/auto';
|
import 'fake-indexeddb/auto';
|
||||||
import 'jest-localstorage-mock';
|
import 'jest-localstorage-mock';
|
||||||
|
|
||||||
import { IndexedDBStore, IStateEventWithRoomId } from "../../../src";
|
import { IndexedDBStore, IStateEventWithRoomId, MemoryStore } from "../../../src";
|
||||||
import { emitPromise } from "../../test-utils/test-utils";
|
import { emitPromise } from "../../test-utils/test-utils";
|
||||||
import { LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend";
|
import { LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend";
|
||||||
|
|
||||||
describe("IndexedDBStore", () => {
|
describe("IndexedDBStore", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
const roomId = "!room:id";
|
||||||
it("should degrade to MemoryStore on IDB errors", async () => {
|
it("should degrade to MemoryStore on IDB errors", async () => {
|
||||||
const roomId = "!room:id";
|
|
||||||
const store = new IndexedDBStore({
|
const store = new IndexedDBStore({
|
||||||
indexedDB: indexedDB,
|
indexedDB: indexedDB,
|
||||||
dbName: "database",
|
dbName: "database",
|
||||||
@@ -69,4 +73,42 @@ describe("IndexedDBStore", () => {
|
|||||||
]);
|
]);
|
||||||
expect(await store.getOutOfBandMembers(roomId)).toHaveLength(2);
|
expect(await store.getOutOfBandMembers(roomId)).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should use MemoryStore methods for pending events if no localStorage", async () => {
|
||||||
|
jest.spyOn(MemoryStore.prototype, "setPendingEvents");
|
||||||
|
jest.spyOn(MemoryStore.prototype, "getPendingEvents");
|
||||||
|
|
||||||
|
const store = new IndexedDBStore({
|
||||||
|
indexedDB: indexedDB,
|
||||||
|
dbName: "database",
|
||||||
|
localStorage: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const events = [{ type: "test" }];
|
||||||
|
await store.setPendingEvents(roomId, events);
|
||||||
|
expect(MemoryStore.prototype.setPendingEvents).toHaveBeenCalledWith(roomId, events);
|
||||||
|
await expect(store.getPendingEvents(roomId)).resolves.toEqual(events);
|
||||||
|
expect(MemoryStore.prototype.getPendingEvents).toHaveBeenCalledWith(roomId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should persist pending events to localStorage if available", async () => {
|
||||||
|
jest.spyOn(MemoryStore.prototype, "setPendingEvents");
|
||||||
|
jest.spyOn(MemoryStore.prototype, "getPendingEvents");
|
||||||
|
|
||||||
|
const store = new IndexedDBStore({
|
||||||
|
indexedDB: indexedDB,
|
||||||
|
dbName: "database",
|
||||||
|
localStorage,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(store.getPendingEvents(roomId)).resolves.toEqual([]);
|
||||||
|
const events = [{ type: "test" }];
|
||||||
|
await store.setPendingEvents(roomId, events);
|
||||||
|
expect(MemoryStore.prototype.setPendingEvents).not.toHaveBeenCalled();
|
||||||
|
await expect(store.getPendingEvents(roomId)).resolves.toEqual(events);
|
||||||
|
expect(MemoryStore.prototype.getPendingEvents).not.toHaveBeenCalled();
|
||||||
|
expect(localStorage.getItem("mx_pending_events_" + roomId)).toBe(JSON.stringify(events));
|
||||||
|
await store.setPendingEvents(roomId, []);
|
||||||
|
expect(localStorage.getItem("mx_pending_events_" + roomId)).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -168,7 +168,6 @@ import {
|
|||||||
import { IAbortablePromise, IdServerUnbindResult, IImageInfo, Preset, Visibility } from "./@types/partials";
|
import { IAbortablePromise, IdServerUnbindResult, IImageInfo, Preset, Visibility } from "./@types/partials";
|
||||||
import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper";
|
import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper";
|
||||||
import { randomString } from "./randomstring";
|
import { randomString } from "./randomstring";
|
||||||
import { WebStorageSessionStore } from "./store/session/webstorage";
|
|
||||||
import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup";
|
import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup";
|
||||||
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace";
|
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace";
|
||||||
import { ISignatures } from "./@types/signed";
|
import { ISignatures } from "./@types/signed";
|
||||||
@@ -195,7 +194,6 @@ import { Thread, THREAD_RELATION_TYPE } from "./models/thread";
|
|||||||
import { MBeaconInfoEventContent, M_BEACON_INFO } from "./@types/beacon";
|
import { MBeaconInfoEventContent, M_BEACON_INFO } from "./@types/beacon";
|
||||||
|
|
||||||
export type Store = IStore;
|
export type Store = IStore;
|
||||||
export type SessionStore = WebStorageSessionStore;
|
|
||||||
|
|
||||||
export type Callback<T = any> = (err: Error | any | null, data?: T) => void;
|
export type Callback<T = any> = (err: Error | any | null, data?: T) => void;
|
||||||
export type ResetTimelineCallback = (roomId: string) => boolean;
|
export type ResetTimelineCallback = (roomId: string) => boolean;
|
||||||
@@ -315,14 +313,6 @@ export interface ICreateClientOpts {
|
|||||||
*/
|
*/
|
||||||
pickleKey?: string;
|
pickleKey?: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* A store to be used for end-to-end crypto session data. Most data has been
|
|
||||||
* migrated out of here to `cryptoStore` instead. If not specified,
|
|
||||||
* end-to-end crypto will be disabled. The `createClient` helper
|
|
||||||
* _will not_ create this store at the moment.
|
|
||||||
*/
|
|
||||||
sessionStore?: SessionStore;
|
|
||||||
|
|
||||||
verificationMethods?: Array<VerificationMethod>;
|
verificationMethods?: Array<VerificationMethod>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -897,7 +887,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
public timelineSupport = false;
|
public timelineSupport = false;
|
||||||
public urlPreviewCache: { [key: string]: Promise<IPreviewUrlResponse> } = {};
|
public urlPreviewCache: { [key: string]: Promise<IPreviewUrlResponse> } = {};
|
||||||
public identityServer: IIdentityServerProvider;
|
public identityServer: IIdentityServerProvider;
|
||||||
public sessionStore: SessionStore; // XXX: Intended private, used in code.
|
|
||||||
public http: MatrixHttpApi; // XXX: Intended private, used in code.
|
public http: MatrixHttpApi; // XXX: Intended private, used in code.
|
||||||
public crypto: Crypto; // XXX: Intended private, used in code.
|
public crypto: Crypto; // XXX: Intended private, used in code.
|
||||||
public cryptoCallbacks: ICryptoCallbacks; // XXX: Intended private, used in code.
|
public cryptoCallbacks: ICryptoCallbacks; // XXX: Intended private, used in code.
|
||||||
@@ -1029,7 +1018,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
this.timelineSupport = Boolean(opts.timelineSupport);
|
this.timelineSupport = Boolean(opts.timelineSupport);
|
||||||
|
|
||||||
this.cryptoStore = opts.cryptoStore;
|
this.cryptoStore = opts.cryptoStore;
|
||||||
this.sessionStore = opts.sessionStore;
|
|
||||||
this.verificationMethods = opts.verificationMethods;
|
this.verificationMethods = opts.verificationMethods;
|
||||||
this.cryptoCallbacks = opts.cryptoCallbacks || {};
|
this.cryptoCallbacks = opts.cryptoCallbacks || {};
|
||||||
|
|
||||||
@@ -1654,10 +1642,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.sessionStore) {
|
|
||||||
// this is temporary, the sessionstore is supposed to be going away
|
|
||||||
throw new Error(`Cannot enable encryption: no sessionStore provided`);
|
|
||||||
}
|
|
||||||
if (!this.cryptoStore) {
|
if (!this.cryptoStore) {
|
||||||
// the cryptostore is provided by sdk.createClient, so this shouldn't happen
|
// the cryptostore is provided by sdk.createClient, so this shouldn't happen
|
||||||
throw new Error(`Cannot enable encryption: no cryptoStore provided`);
|
throw new Error(`Cannot enable encryption: no cryptoStore provided`);
|
||||||
@@ -1686,8 +1670,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
|
|
||||||
const crypto = new Crypto(
|
const crypto = new Crypto(
|
||||||
this,
|
this,
|
||||||
this.sessionStore,
|
userId,
|
||||||
userId, this.deviceId,
|
this.deviceId,
|
||||||
this.store,
|
this.store,
|
||||||
this.cryptoStore,
|
this.cryptoStore,
|
||||||
this.roomList,
|
this.roomList,
|
||||||
|
@@ -75,7 +75,6 @@ import {
|
|||||||
ISignedKey,
|
ISignedKey,
|
||||||
IUploadKeySignaturesResponse,
|
IUploadKeySignaturesResponse,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
SessionStore,
|
|
||||||
} from "../client";
|
} from "../client";
|
||||||
import type { IRoomEncryption, RoomList } from "./RoomList";
|
import type { IRoomEncryption, RoomList } from "./RoomList";
|
||||||
import { IKeyBackupInfo } from "./keybackup";
|
import { IKeyBackupInfo } from "./keybackup";
|
||||||
@@ -323,9 +322,6 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
*
|
*
|
||||||
* @param {MatrixClient} baseApis base matrix api interface
|
* @param {MatrixClient} baseApis base matrix api interface
|
||||||
*
|
*
|
||||||
* @param {module:store/session/webstorage~WebStorageSessionStore} sessionStore
|
|
||||||
* Store to be used for end-to-end crypto session data
|
|
||||||
*
|
|
||||||
* @param {string} userId The user ID for the local user
|
* @param {string} userId The user ID for the local user
|
||||||
*
|
*
|
||||||
* @param {string} deviceId The identifier for this device.
|
* @param {string} deviceId The identifier for this device.
|
||||||
@@ -343,7 +339,6 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
public readonly baseApis: MatrixClient,
|
public readonly baseApis: MatrixClient,
|
||||||
public readonly sessionStore: SessionStore,
|
|
||||||
public readonly userId: string,
|
public readonly userId: string,
|
||||||
private readonly deviceId: string,
|
private readonly deviceId: string,
|
||||||
private readonly clientStore: IStore,
|
private readonly clientStore: IStore,
|
||||||
@@ -1725,13 +1720,6 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
logger.info(`Finished device verification upgrade for ${userId}`);
|
logger.info(`Finished device verification upgrade for ${userId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setTrustedBackupPubKey(trustedPubKey: string): Promise<void> {
|
|
||||||
// This should be redundant post cross-signing is a thing, so just
|
|
||||||
// plonk it in localStorage for now.
|
|
||||||
this.sessionStore.setLocalTrustedBackupPubKey(trustedPubKey);
|
|
||||||
await this.backupManager.checkKeyBackup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public enableLazyLoading(): void {
|
public enableLazyLoading(): void {
|
||||||
|
@@ -157,7 +157,7 @@ export class IndexedDBCryptoStore implements CryptoStore {
|
|||||||
}
|
}
|
||||||
}).then(backend => {
|
}).then(backend => {
|
||||||
this.backend = backend;
|
this.backend = backend;
|
||||||
return backend as CryptoStore;
|
return backend;
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.backendPromise;
|
return this.backendPromise;
|
||||||
|
@@ -41,7 +41,6 @@ export * from "./interactive-auth";
|
|||||||
export * from "./service-types";
|
export * from "./service-types";
|
||||||
export * from "./store/memory";
|
export * from "./store/memory";
|
||||||
export * from "./store/indexeddb";
|
export * from "./store/indexeddb";
|
||||||
export * from "./store/session/webstorage";
|
|
||||||
export * from "./crypto/store/memory-crypto-store";
|
export * from "./crypto/store/memory-crypto-store";
|
||||||
export * from "./crypto/store/indexeddb-crypto-store";
|
export * from "./crypto/store/indexeddb-crypto-store";
|
||||||
export * from "./content-repo";
|
export * from "./content-repo";
|
||||||
|
@@ -368,18 +368,16 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
|
|||||||
|
|
||||||
if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached) {
|
if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached) {
|
||||||
this.pendingEventList = [];
|
this.pendingEventList = [];
|
||||||
const serializedPendingEventList = client.sessionStore.store.getItem(pendingEventsKey(this.roomId));
|
this.client.store.getPendingEvents(this.roomId).then(events => {
|
||||||
if (serializedPendingEventList) {
|
events.forEach(async (serializedEvent: Partial<IEvent>) => {
|
||||||
JSON.parse(serializedPendingEventList)
|
const event = new MatrixEvent(serializedEvent);
|
||||||
.forEach(async (serializedEvent: Partial<IEvent>) => {
|
if (event.getType() === EventType.RoomMessageEncrypted) {
|
||||||
const event = new MatrixEvent(serializedEvent);
|
await event.attemptDecryption(this.client.crypto);
|
||||||
if (event.getType() === EventType.RoomMessageEncrypted) {
|
}
|
||||||
await event.attemptDecryption(this.client.crypto);
|
event.setStatus(EventStatus.NOT_SENT);
|
||||||
}
|
this.addPendingEvent(event, event.getTxnId());
|
||||||
event.setStatus(EventStatus.NOT_SENT);
|
});
|
||||||
this.addPendingEvent(event, event.getTxnId());
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// awaited by getEncryptionTargetMembers while room members are loading
|
// awaited by getEncryptionTargetMembers while room members are loading
|
||||||
@@ -2075,15 +2073,7 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
|
|||||||
return isEventEncrypted || !isRoomEncrypted;
|
return isEventEncrypted || !isRoomEncrypted;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { store } = this.client.sessionStore;
|
this.client.store.setPendingEvents(this.roomId, pendingEvents);
|
||||||
if (this.pendingEventList.length > 0) {
|
|
||||||
store.setItem(
|
|
||||||
pendingEventsKey(this.roomId),
|
|
||||||
JSON.stringify(pendingEvents),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
store.removeItem(pendingEventsKey(this.roomId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3112,14 +3102,6 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} roomId ID of the current room
|
|
||||||
* @returns {string} Storage key to retrieve pending events
|
|
||||||
*/
|
|
||||||
function pendingEventsKey(roomId: string): string {
|
|
||||||
return `mx_pending_events_${roomId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// a map from current event status to a list of allowed next statuses
|
// a map from current event status to a list of allowed next statuses
|
||||||
const ALLOWED_TRANSITIONS: Record<EventStatus, EventStatus[]> = {
|
const ALLOWED_TRANSITIONS: Record<EventStatus, EventStatus[]> = {
|
||||||
[EventStatus.ENCRYPTING]: [
|
[EventStatus.ENCRYPTING]: [
|
||||||
|
@@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
import { EventType } from "../@types/event";
|
import { EventType } from "../@types/event";
|
||||||
import { Room } from "../models/room";
|
import { Room } from "../models/room";
|
||||||
import { User } from "../models/user";
|
import { User } from "../models/user";
|
||||||
import { MatrixEvent } from "../models/event";
|
import { IEvent, MatrixEvent } from "../models/event";
|
||||||
import { Filter } from "../filter";
|
import { Filter } from "../filter";
|
||||||
import { RoomSummary } from "../models/room-summary";
|
import { RoomSummary } from "../models/room-summary";
|
||||||
import { IMinimalEvent, IRooms, ISyncResponse } from "../sync-accumulator";
|
import { IMinimalEvent, IRooms, ISyncResponse } from "../sync-accumulator";
|
||||||
@@ -218,4 +218,8 @@ export interface IStore {
|
|||||||
getClientOptions(): Promise<IStartClientOpts>;
|
getClientOptions(): Promise<IStartClientOpts>;
|
||||||
|
|
||||||
storeClientOptions(options: IStartClientOpts): Promise<void>;
|
storeClientOptions(options: IStartClientOpts): Promise<void>;
|
||||||
|
|
||||||
|
getPendingEvents(roomId: string): Promise<Partial<IEvent>[]>;
|
||||||
|
|
||||||
|
setPendingEvents(roomId: string, events: Partial<IEvent>[]): Promise<void>;
|
||||||
}
|
}
|
||||||
|
@@ -325,6 +325,40 @@ export class IndexedDBStore extends MemoryStore {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: ideally these would be stored in indexeddb as part of the room but,
|
||||||
|
// we don't store rooms as such and instead accumulate entire sync responses atm.
|
||||||
|
public async getPendingEvents(roomId: string): Promise<Partial<IEvent>[]> {
|
||||||
|
if (!this.localStorage) return super.getPendingEvents(roomId);
|
||||||
|
|
||||||
|
const serialized = this.localStorage.getItem(pendingEventsKey(roomId));
|
||||||
|
if (serialized) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(serialized);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Could not parse persisted pending events", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setPendingEvents(roomId: string, events: Partial<IEvent>[]): Promise<void> {
|
||||||
|
if (!this.localStorage) return super.setPendingEvents(roomId, events);
|
||||||
|
|
||||||
|
if (events.length > 0) {
|
||||||
|
this.localStorage.setItem(pendingEventsKey(roomId), JSON.stringify(events));
|
||||||
|
} else {
|
||||||
|
this.localStorage.removeItem(pendingEventsKey(roomId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} roomId ID of the current room
|
||||||
|
* @returns {string} Storage key to retrieve pending events
|
||||||
|
*/
|
||||||
|
function pendingEventsKey(roomId: string): string {
|
||||||
|
return `mx_pending_events_${roomId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
type DegradableFn<A extends Array<any>, T> = (...args: A) => Promise<T>;
|
type DegradableFn<A extends Array<any>, T> = (...args: A) => Promise<T>;
|
||||||
|
@@ -22,7 +22,7 @@ limitations under the License.
|
|||||||
import { EventType } from "../@types/event";
|
import { EventType } from "../@types/event";
|
||||||
import { Room } from "../models/room";
|
import { Room } from "../models/room";
|
||||||
import { User } from "../models/user";
|
import { User } from "../models/user";
|
||||||
import { MatrixEvent } from "../models/event";
|
import { IEvent, MatrixEvent } from "../models/event";
|
||||||
import { RoomState, RoomStateEvent } from "../models/room-state";
|
import { RoomState, RoomStateEvent } from "../models/room-state";
|
||||||
import { RoomMember } from "../models/room-member";
|
import { RoomMember } from "../models/room-member";
|
||||||
import { Filter } from "../filter";
|
import { Filter } from "../filter";
|
||||||
@@ -48,7 +48,7 @@ export interface IOpts {
|
|||||||
* Construct a new in-memory data store for the Matrix Client.
|
* Construct a new in-memory data store for the Matrix Client.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Object=} opts Config options
|
* @param {Object=} opts Config options
|
||||||
* @param {LocalStorage} opts.localStorage The local storage instance to persist
|
* @param {Storage} opts.localStorage The local storage instance to persist
|
||||||
* some forms of data such as tokens. Rooms will NOT be stored.
|
* some forms of data such as tokens. Rooms will NOT be stored.
|
||||||
*/
|
*/
|
||||||
export class MemoryStore implements IStore {
|
export class MemoryStore implements IStore {
|
||||||
@@ -60,8 +60,9 @@ export class MemoryStore implements IStore {
|
|||||||
// }
|
// }
|
||||||
private filters: Record<string, Record<string, Filter>> = {};
|
private filters: Record<string, Record<string, Filter>> = {};
|
||||||
public accountData: Record<string, MatrixEvent> = {}; // type : content
|
public accountData: Record<string, MatrixEvent> = {}; // type : content
|
||||||
private readonly localStorage: Storage;
|
protected readonly localStorage: Storage;
|
||||||
private oobMembers: Record<string, IStateEventWithRoomId[]> = {}; // roomId: [member events]
|
private oobMembers: Record<string, IStateEventWithRoomId[]> = {}; // roomId: [member events]
|
||||||
|
private pendingEvents: { [roomId: string]: Partial<IEvent>[] } = {};
|
||||||
private clientOptions = {};
|
private clientOptions = {};
|
||||||
|
|
||||||
constructor(opts: IOpts = {}) {
|
constructor(opts: IOpts = {}) {
|
||||||
@@ -420,4 +421,12 @@ export class MemoryStore implements IStore {
|
|||||||
this.clientOptions = Object.assign({}, options);
|
this.clientOptions = Object.assign({}, options);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getPendingEvents(roomId: string): Promise<Partial<IEvent>[]> {
|
||||||
|
return this.pendingEvents[roomId] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setPendingEvents(roomId: string, events: Partial<IEvent>[]): Promise<void> {
|
||||||
|
this.pendingEvents[roomId] = events;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,263 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
Copyright 2017 New Vector Ltd
|
|
||||||
Copyright 2018 New Vector Ltd
|
|
||||||
Copyright 2019 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module store/session/webstorage
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as utils from "../../utils";
|
|
||||||
import { logger } from '../../logger';
|
|
||||||
|
|
||||||
const DEBUG = false; // set true to enable console logging.
|
|
||||||
const E2E_PREFIX = "session.e2e.";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a web storage session store, capable of storing account keys,
|
|
||||||
* session keys and access tokens.
|
|
||||||
* @constructor
|
|
||||||
* @param {WebStorage} webStore A web storage implementation, e.g.
|
|
||||||
* 'window.localStorage' or 'window.sessionStorage' or a custom implementation.
|
|
||||||
* @throws if the supplied 'store' does not meet the Storage interface of the
|
|
||||||
* WebStorage API.
|
|
||||||
*/
|
|
||||||
export function WebStorageSessionStore(webStore) {
|
|
||||||
this.store = webStore;
|
|
||||||
if (!utils.isFunction(webStore.getItem) ||
|
|
||||||
!utils.isFunction(webStore.setItem) ||
|
|
||||||
!utils.isFunction(webStore.removeItem) ||
|
|
||||||
!utils.isFunction(webStore.key) ||
|
|
||||||
typeof(webStore.length) !== 'number'
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
"Supplied webStore does not meet the WebStorage API interface",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WebStorageSessionStore.prototype = {
|
|
||||||
/**
|
|
||||||
* Remove the stored end to end account for the logged-in user.
|
|
||||||
*/
|
|
||||||
removeEndToEndAccount: function() {
|
|
||||||
this.store.removeItem(KEY_END_TO_END_ACCOUNT);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the end to end account for the logged-in user.
|
|
||||||
* Note that the end-to-end account is now stored in the
|
|
||||||
* crypto store rather than here: this remains here so
|
|
||||||
* old sessions can be migrated out of the session store.
|
|
||||||
* @return {?string} Base64 encoded account.
|
|
||||||
*/
|
|
||||||
getEndToEndAccount: function() {
|
|
||||||
return this.store.getItem(KEY_END_TO_END_ACCOUNT);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the known devices for all users.
|
|
||||||
* @return {object} A map from user ID to map of device ID to keys for the device.
|
|
||||||
*/
|
|
||||||
getAllEndToEndDevices: function() {
|
|
||||||
const prefix = keyEndToEndDevicesForUser('');
|
|
||||||
const devices = {};
|
|
||||||
for (let i = 0; i < this.store.length; ++i) {
|
|
||||||
const key = this.store.key(i);
|
|
||||||
const userId = key.slice(prefix.length);
|
|
||||||
if (key.startsWith(prefix)) devices[userId] = getJsonItem(this.store, key);
|
|
||||||
}
|
|
||||||
return devices;
|
|
||||||
},
|
|
||||||
|
|
||||||
getEndToEndDeviceTrackingStatus: function() {
|
|
||||||
return getJsonItem(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the sync token corresponding to the device list.
|
|
||||||
*
|
|
||||||
* @return {String?} token
|
|
||||||
*/
|
|
||||||
getEndToEndDeviceSyncToken: function() {
|
|
||||||
return getJsonItem(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all end to end device data from the store
|
|
||||||
*/
|
|
||||||
removeEndToEndDeviceData: function() {
|
|
||||||
removeByPrefix(this.store, keyEndToEndDevicesForUser(''));
|
|
||||||
removeByPrefix(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS);
|
|
||||||
removeByPrefix(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the end-to-end sessions between the logged-in user and another
|
|
||||||
* device.
|
|
||||||
* @param {string} deviceKey The public key of the other device.
|
|
||||||
* @return {object} A map from sessionId to Base64 end-to-end session.
|
|
||||||
*/
|
|
||||||
getEndToEndSessions: function(deviceKey) {
|
|
||||||
return getJsonItem(this.store, keyEndToEndSessions(deviceKey));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all end-to-end sessions between the logged-in user and other
|
|
||||||
* devices.
|
|
||||||
* @return {object} A map of {deviceKey -> {sessionId -> session pickle}}
|
|
||||||
*/
|
|
||||||
getAllEndToEndSessions: function() {
|
|
||||||
const deviceKeys = getKeysWithPrefix(this.store, keyEndToEndSessions(''));
|
|
||||||
const results = {};
|
|
||||||
for (const k of deviceKeys) {
|
|
||||||
const unprefixedKey = k.slice(keyEndToEndSessions('').length);
|
|
||||||
results[unprefixedKey] = getJsonItem(this.store, k);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all end-to-end sessions from the store
|
|
||||||
* This is used after migrating sessions awat from the sessions store.
|
|
||||||
*/
|
|
||||||
removeAllEndToEndSessions: function() {
|
|
||||||
removeByPrefix(this.store, keyEndToEndSessions(''));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a list of all known inbound group sessions
|
|
||||||
*
|
|
||||||
* @return {{senderKey: string, sessionId: string}}
|
|
||||||
*/
|
|
||||||
getAllEndToEndInboundGroupSessionKeys: function() {
|
|
||||||
const prefix = E2E_PREFIX + 'inboundgroupsessions/';
|
|
||||||
const result = [];
|
|
||||||
for (let i = 0; i < this.store.length; i++) {
|
|
||||||
const key = this.store.key(i);
|
|
||||||
if (!key.startsWith(prefix)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// we can't use split, as the components we are trying to split out
|
|
||||||
// might themselves contain '/' characters. We rely on the
|
|
||||||
// senderKey being a (32-byte) curve25519 key, base64-encoded
|
|
||||||
// (hence 43 characters long).
|
|
||||||
|
|
||||||
result.push({
|
|
||||||
senderKey: key.slice(prefix.length, prefix.length + 43),
|
|
||||||
sessionId: key.slice(prefix.length + 44),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
getEndToEndInboundGroupSession: function(senderKey, sessionId) {
|
|
||||||
const key = keyEndToEndInboundGroupSession(senderKey, sessionId);
|
|
||||||
return this.store.getItem(key);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeAllEndToEndInboundGroupSessions: function() {
|
|
||||||
removeByPrefix(this.store, E2E_PREFIX + 'inboundgroupsessions/');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the end-to-end state for all rooms
|
|
||||||
* @return {object} roomId -> object with the end-to-end info for the room.
|
|
||||||
*/
|
|
||||||
getAllEndToEndRooms: function() {
|
|
||||||
const roomKeys = getKeysWithPrefix(this.store, keyEndToEndRoom(''));
|
|
||||||
const results = {};
|
|
||||||
for (const k of roomKeys) {
|
|
||||||
const unprefixedKey = k.slice(keyEndToEndRoom('').length);
|
|
||||||
results[unprefixedKey] = getJsonItem(this.store, k);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeAllEndToEndRooms: function() {
|
|
||||||
removeByPrefix(this.store, keyEndToEndRoom(''));
|
|
||||||
},
|
|
||||||
|
|
||||||
setLocalTrustedBackupPubKey: function(pubkey) {
|
|
||||||
this.store.setItem(KEY_END_TO_END_TRUSTED_BACKUP_PUBKEY, pubkey);
|
|
||||||
},
|
|
||||||
|
|
||||||
// XXX: This store is deprecated really, but added this as a temporary
|
|
||||||
// thing until cross-signing lands.
|
|
||||||
getLocalTrustedBackupPubKey: function() {
|
|
||||||
return this.store.getItem(KEY_END_TO_END_TRUSTED_BACKUP_PUBKEY);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
|
|
||||||
const KEY_END_TO_END_DEVICE_SYNC_TOKEN = E2E_PREFIX + "device_sync_token";
|
|
||||||
const KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS = E2E_PREFIX + "device_tracking";
|
|
||||||
const KEY_END_TO_END_TRUSTED_BACKUP_PUBKEY = E2E_PREFIX + "trusted_backup_pubkey";
|
|
||||||
|
|
||||||
function keyEndToEndDevicesForUser(userId) {
|
|
||||||
return E2E_PREFIX + "devices/" + userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyEndToEndSessions(deviceKey) {
|
|
||||||
return E2E_PREFIX + "sessions/" + deviceKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyEndToEndInboundGroupSession(senderKey, sessionId) {
|
|
||||||
return E2E_PREFIX + "inboundgroupsessions/" + senderKey + "/" + sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyEndToEndRoom(roomId) {
|
|
||||||
return E2E_PREFIX + "rooms/" + roomId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getJsonItem(store, key) {
|
|
||||||
try {
|
|
||||||
// if the key is absent, store.getItem() returns null, and
|
|
||||||
// JSON.parse(null) === null, so this returns null.
|
|
||||||
return JSON.parse(store.getItem(key));
|
|
||||||
} catch (e) {
|
|
||||||
debuglog("Failed to get key %s: %s", key, e);
|
|
||||||
debuglog(e.stack);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getKeysWithPrefix(store, prefix) {
|
|
||||||
const results = [];
|
|
||||||
for (let i = 0; i < store.length; ++i) {
|
|
||||||
const key = store.key(i);
|
|
||||||
if (key.startsWith(prefix)) results.push(key);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeByPrefix(store, prefix) {
|
|
||||||
const toRemove = [];
|
|
||||||
for (let i = 0; i < store.length; ++i) {
|
|
||||||
const key = store.key(i);
|
|
||||||
if (key.startsWith(prefix)) toRemove.push(key);
|
|
||||||
}
|
|
||||||
for (const key of toRemove) {
|
|
||||||
store.removeItem(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function debuglog(...args) {
|
|
||||||
if (DEBUG) {
|
|
||||||
logger.log(...args);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -22,7 +22,7 @@ limitations under the License.
|
|||||||
import { EventType } from "../@types/event";
|
import { EventType } from "../@types/event";
|
||||||
import { Room } from "../models/room";
|
import { Room } from "../models/room";
|
||||||
import { User } from "../models/user";
|
import { User } from "../models/user";
|
||||||
import { MatrixEvent } from "../models/event";
|
import { IEvent, MatrixEvent } from "../models/event";
|
||||||
import { Filter } from "../filter";
|
import { Filter } from "../filter";
|
||||||
import { ISavedSync, IStore } from "./index";
|
import { ISavedSync, IStore } from "./index";
|
||||||
import { RoomSummary } from "../models/room-summary";
|
import { RoomSummary } from "../models/room-summary";
|
||||||
@@ -262,4 +262,12 @@ export class StubStore implements IStore {
|
|||||||
public storeClientOptions(options: object): Promise<void> {
|
public storeClientOptions(options: object): Promise<void> {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getPendingEvents(roomId: string): Promise<Partial<IEvent>[]> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPendingEvents(roomId: string, events: Partial<IEvent>[]): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user