summaryrefslogtreecommitdiff
path: root/includes/external/matrix/node_modules/matrix-js-sdk/lib/sliding-sync-sdk.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/external/matrix/node_modules/matrix-js-sdk/lib/sliding-sync-sdk.js')
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/lib/sliding-sync-sdk.js881
1 files changed, 0 insertions, 881 deletions
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/lib/sliding-sync-sdk.js b/includes/external/matrix/node_modules/matrix-js-sdk/lib/sliding-sync-sdk.js
deleted file mode 100644
index 715187e..0000000
--- a/includes/external/matrix/node_modules/matrix-js-sdk/lib/sliding-sync-sdk.js
+++ /dev/null
@@ -1,881 +0,0 @@
-"use strict";
-
-var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.SlidingSyncSdk = void 0;
-var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
-var _room = require("./models/room");
-var _logger = require("./logger");
-var utils = _interopRequireWildcard(require("./utils"));
-var _eventTimeline = require("./models/event-timeline");
-var _client = require("./client");
-var _sync = require("./sync");
-var _httpApi = require("./http-api");
-var _slidingSync = require("./sliding-sync");
-var _event = require("./@types/event");
-var _roomState = require("./models/room-state");
-var _roomMember = require("./models/room-member");
-function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
-function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
-/*
-Copyright 2022 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.
-*/
-
-// Number of consecutive failed syncs that will lead to a syncState of ERROR as opposed
-// to RECONNECTING. This is needed to inform the client of server issues when the
-// keepAlive is successful but the server /sync fails.
-const FAILED_SYNC_ERROR_THRESHOLD = 3;
-class ExtensionE2EE {
- constructor(crypto) {
- this.crypto = crypto;
- }
- name() {
- return "e2ee";
- }
- when() {
- return _slidingSync.ExtensionState.PreProcess;
- }
- onRequest(isInitial) {
- if (!isInitial) {
- return undefined;
- }
- return {
- enabled: true // this is sticky so only send it on the initial request
- };
- }
-
- async onResponse(data) {
- // Handle device list updates
- if (data["device_lists"]) {
- await this.crypto.handleDeviceListChanges({
- oldSyncToken: "yep" // XXX need to do this so the device list changes get processed :(
- }, data["device_lists"]);
- }
-
- // Handle one_time_keys_count
- if (data["device_one_time_keys_count"]) {
- const currentCount = data["device_one_time_keys_count"].signed_curve25519 || 0;
- this.crypto.updateOneTimeKeyCount(currentCount);
- }
- if (data["device_unused_fallback_key_types"] || data["org.matrix.msc2732.device_unused_fallback_key_types"]) {
- // The presence of device_unused_fallback_key_types indicates that the
- // server supports fallback keys. If there's no unused
- // signed_curve25519 fallback key we need a new one.
- const unusedFallbackKeys = data["device_unused_fallback_key_types"] || data["org.matrix.msc2732.device_unused_fallback_key_types"];
- this.crypto.setNeedsNewFallback(Array.isArray(unusedFallbackKeys) && !unusedFallbackKeys.includes("signed_curve25519"));
- }
- this.crypto.onSyncCompleted({});
- }
-}
-class ExtensionToDevice {
- constructor(client, cryptoCallbacks) {
- this.client = client;
- this.cryptoCallbacks = cryptoCallbacks;
- (0, _defineProperty2.default)(this, "nextBatch", null);
- }
- name() {
- return "to_device";
- }
- when() {
- return _slidingSync.ExtensionState.PreProcess;
- }
- onRequest(isInitial) {
- const extReq = {
- since: this.nextBatch !== null ? this.nextBatch : undefined
- };
- if (isInitial) {
- extReq["limit"] = 100;
- extReq["enabled"] = true;
- }
- return extReq;
- }
- async onResponse(data) {
- const cancelledKeyVerificationTxns = [];
- let events = data["events"] || [];
- if (events.length > 0 && this.cryptoCallbacks) {
- events = await this.cryptoCallbacks.preprocessToDeviceMessages(events);
- }
- events.map(this.client.getEventMapper()).map(toDeviceEvent => {
- // map is a cheap inline forEach
- // We want to flag m.key.verification.start events as cancelled
- // if there's an accompanying m.key.verification.cancel event, so
- // we pull out the transaction IDs from the cancellation events
- // so we can flag the verification events as cancelled in the loop
- // below.
- if (toDeviceEvent.getType() === "m.key.verification.cancel") {
- const txnId = toDeviceEvent.getContent()["transaction_id"];
- if (txnId) {
- cancelledKeyVerificationTxns.push(txnId);
- }
- }
-
- // as mentioned above, .map is a cheap inline forEach, so return
- // the unmodified event.
- return toDeviceEvent;
- }).forEach(toDeviceEvent => {
- const content = toDeviceEvent.getContent();
- if (toDeviceEvent.getType() == "m.room.message" && content.msgtype == "m.bad.encrypted") {
- // the mapper already logged a warning.
- _logger.logger.log("Ignoring undecryptable to-device event from " + toDeviceEvent.getSender());
- return;
- }
- if (toDeviceEvent.getType() === "m.key.verification.start" || toDeviceEvent.getType() === "m.key.verification.request") {
- const txnId = content["transaction_id"];
- if (cancelledKeyVerificationTxns.includes(txnId)) {
- toDeviceEvent.flagCancelled();
- }
- }
- this.client.emit(_client.ClientEvent.ToDeviceEvent, toDeviceEvent);
- });
- this.nextBatch = data.next_batch;
- }
-}
-class ExtensionAccountData {
- constructor(client) {
- this.client = client;
- }
- name() {
- return "account_data";
- }
- when() {
- return _slidingSync.ExtensionState.PostProcess;
- }
- onRequest(isInitial) {
- if (!isInitial) {
- return undefined;
- }
- return {
- enabled: true
- };
- }
- onResponse(data) {
- if (data.global && data.global.length > 0) {
- this.processGlobalAccountData(data.global);
- }
- for (const roomId in data.rooms) {
- const accountDataEvents = mapEvents(this.client, roomId, data.rooms[roomId]);
- const room = this.client.getRoom(roomId);
- if (!room) {
- _logger.logger.warn("got account data for room but room doesn't exist on client:", roomId);
- continue;
- }
- room.addAccountData(accountDataEvents);
- accountDataEvents.forEach(e => {
- this.client.emit(_client.ClientEvent.Event, e);
- });
- }
- }
- processGlobalAccountData(globalAccountData) {
- const events = mapEvents(this.client, undefined, globalAccountData);
- const prevEventsMap = events.reduce((m, c) => {
- m[c.getType()] = this.client.store.getAccountData(c.getType());
- return m;
- }, {});
- this.client.store.storeAccountDataEvents(events);
- events.forEach(accountDataEvent => {
- // Honour push rules that come down the sync stream but also
- // honour push rules that were previously cached. Base rules
- // will be updated when we receive push rules via getPushRules
- // (see sync) before syncing over the network.
- if (accountDataEvent.getType() === _event.EventType.PushRules) {
- const rules = accountDataEvent.getContent();
- this.client.setPushRules(rules);
- }
- const prevEvent = prevEventsMap[accountDataEvent.getType()];
- this.client.emit(_client.ClientEvent.AccountData, accountDataEvent, prevEvent);
- return accountDataEvent;
- });
- }
-}
-class ExtensionTyping {
- constructor(client) {
- this.client = client;
- }
- name() {
- return "typing";
- }
- when() {
- return _slidingSync.ExtensionState.PostProcess;
- }
- onRequest(isInitial) {
- if (!isInitial) {
- return undefined; // don't send a JSON object for subsequent requests, we don't need to.
- }
-
- return {
- enabled: true
- };
- }
- onResponse(data) {
- if (!(data !== null && data !== void 0 && data.rooms)) {
- return;
- }
- for (const roomId in data.rooms) {
- processEphemeralEvents(this.client, roomId, [data.rooms[roomId]]);
- }
- }
-}
-class ExtensionReceipts {
- constructor(client) {
- this.client = client;
- }
- name() {
- return "receipts";
- }
- when() {
- return _slidingSync.ExtensionState.PostProcess;
- }
- onRequest(isInitial) {
- if (isInitial) {
- return {
- enabled: true
- };
- }
- return undefined; // don't send a JSON object for subsequent requests, we don't need to.
- }
-
- onResponse(data) {
- if (!(data !== null && data !== void 0 && data.rooms)) {
- return;
- }
- for (const roomId in data.rooms) {
- processEphemeralEvents(this.client, roomId, [data.rooms[roomId]]);
- }
- }
-}
-
-/**
- * A copy of SyncApi such that it can be used as a drop-in replacement for sync v2. For the actual
- * sliding sync API, see sliding-sync.ts or the class SlidingSync.
- */
-class SlidingSyncSdk {
- // accumulator of sync events in the current sync response
-
- constructor(slidingSync, client, opts, syncOpts) {
- this.slidingSync = slidingSync;
- this.client = client;
- (0, _defineProperty2.default)(this, "opts", void 0);
- (0, _defineProperty2.default)(this, "syncOpts", void 0);
- (0, _defineProperty2.default)(this, "syncState", null);
- (0, _defineProperty2.default)(this, "syncStateData", void 0);
- (0, _defineProperty2.default)(this, "lastPos", null);
- (0, _defineProperty2.default)(this, "failCount", 0);
- (0, _defineProperty2.default)(this, "notifEvents", []);
- this.opts = (0, _sync.defaultClientOpts)(opts);
- this.syncOpts = (0, _sync.defaultSyncApiOpts)(syncOpts);
- if (client.getNotifTimelineSet()) {
- client.reEmitter.reEmit(client.getNotifTimelineSet(), [_room.RoomEvent.Timeline, _room.RoomEvent.TimelineReset]);
- }
- this.slidingSync.on(_slidingSync.SlidingSyncEvent.Lifecycle, this.onLifecycle.bind(this));
- this.slidingSync.on(_slidingSync.SlidingSyncEvent.RoomData, this.onRoomData.bind(this));
- const extensions = [new ExtensionToDevice(this.client, this.syncOpts.cryptoCallbacks), new ExtensionAccountData(this.client), new ExtensionTyping(this.client), new ExtensionReceipts(this.client)];
- if (this.syncOpts.crypto) {
- extensions.push(new ExtensionE2EE(this.syncOpts.crypto));
- }
- extensions.forEach(ext => {
- this.slidingSync.registerExtension(ext);
- });
- }
- onRoomData(roomId, roomData) {
- let room = this.client.store.getRoom(roomId);
- if (!room) {
- if (!roomData.initial) {
- _logger.logger.debug("initial flag not set but no stored room exists for room ", roomId, roomData);
- return;
- }
- room = (0, _sync._createAndReEmitRoom)(this.client, roomId, this.opts);
- }
- this.processRoomData(this.client, room, roomData);
- }
- onLifecycle(state, resp, err) {
- if (err) {
- _logger.logger.debug("onLifecycle", state, err);
- }
- switch (state) {
- case _slidingSync.SlidingSyncState.Complete:
- this.purgeNotifications();
- if (!resp) {
- break;
- }
- // Element won't stop showing the initial loading spinner unless we fire SyncState.Prepared
- if (!this.lastPos) {
- this.updateSyncState(_sync.SyncState.Prepared, {
- oldSyncToken: undefined,
- nextSyncToken: resp.pos,
- catchingUp: false,
- fromCache: false
- });
- }
- // Conversely, Element won't show the room list unless there is at least 1x SyncState.Syncing
- // so hence for the very first sync we will fire prepared then immediately syncing.
- this.updateSyncState(_sync.SyncState.Syncing, {
- oldSyncToken: this.lastPos,
- nextSyncToken: resp.pos,
- catchingUp: false,
- fromCache: false
- });
- this.lastPos = resp.pos;
- break;
- case _slidingSync.SlidingSyncState.RequestFinished:
- if (err) {
- this.failCount += 1;
- this.updateSyncState(this.failCount > FAILED_SYNC_ERROR_THRESHOLD ? _sync.SyncState.Error : _sync.SyncState.Reconnecting, {
- error: new _httpApi.MatrixError(err)
- });
- if (this.shouldAbortSync(new _httpApi.MatrixError(err))) {
- return; // shouldAbortSync actually stops syncing too so we don't need to do anything.
- }
- } else {
- this.failCount = 0;
- }
- break;
- }
- }
-
- /**
- * Sync rooms the user has left.
- * @returns Resolved when they've been added to the store.
- */
- async syncLeftRooms() {
- return []; // TODO
- }
-
- /**
- * Peek into a room. This will result in the room in question being synced so it
- * is accessible via getRooms(). Live updates for the room will be provided.
- * @param roomId - The room ID to peek into.
- * @returns A promise which resolves once the room has been added to the
- * store.
- */
- async peek(_roomId) {
- return null; // TODO
- }
-
- /**
- * Stop polling for updates in the peeked room. NOPs if there is no room being
- * peeked.
- */
- stopPeeking() {
- // TODO
- }
-
- /**
- * Returns the current state of this sync object
- * @see MatrixClient#event:"sync"
- */
- getSyncState() {
- return this.syncState;
- }
-
- /**
- * Returns the additional data object associated with
- * the current sync state, or null if there is no
- * such data.
- * Sync errors, if available, are put in the 'error' key of
- * this object.
- */
- getSyncStateData() {
- var _this$syncStateData;
- return (_this$syncStateData = this.syncStateData) !== null && _this$syncStateData !== void 0 ? _this$syncStateData : null;
- }
-
- // Helper functions which set up JS SDK structs are below and are identical to the sync v2 counterparts
-
- createRoom(roomId) {
- // XXX cargoculted from sync.ts
- const {
- timelineSupport
- } = this.client;
- const room = new _room.Room(roomId, this.client, this.client.getUserId(), {
- lazyLoadMembers: this.opts.lazyLoadMembers,
- pendingEventOrdering: this.opts.pendingEventOrdering,
- timelineSupport
- });
- this.client.reEmitter.reEmit(room, [_room.RoomEvent.Name, _room.RoomEvent.Redaction, _room.RoomEvent.RedactionCancelled, _room.RoomEvent.Receipt, _room.RoomEvent.Tags, _room.RoomEvent.LocalEchoUpdated, _room.RoomEvent.AccountData, _room.RoomEvent.MyMembership, _room.RoomEvent.Timeline, _room.RoomEvent.TimelineReset]);
- this.registerStateListeners(room);
- return room;
- }
- registerStateListeners(room) {
- // XXX cargoculted from sync.ts
- // we need to also re-emit room state and room member events, so hook it up
- // to the client now. We need to add a listener for RoomState.members in
- // order to hook them correctly.
- this.client.reEmitter.reEmit(room.currentState, [_roomState.RoomStateEvent.Events, _roomState.RoomStateEvent.Members, _roomState.RoomStateEvent.NewMember, _roomState.RoomStateEvent.Update]);
- room.currentState.on(_roomState.RoomStateEvent.NewMember, (event, state, member) => {
- var _this$client$getUser;
- member.user = (_this$client$getUser = this.client.getUser(member.userId)) !== null && _this$client$getUser !== void 0 ? _this$client$getUser : undefined;
- this.client.reEmitter.reEmit(member, [_roomMember.RoomMemberEvent.Name, _roomMember.RoomMemberEvent.Typing, _roomMember.RoomMemberEvent.PowerLevel, _roomMember.RoomMemberEvent.Membership]);
- });
- }
-
- /*
- private deregisterStateListeners(room: Room): void { // XXX cargoculted from sync.ts
- // could do with a better way of achieving this.
- room.currentState.removeAllListeners(RoomStateEvent.Events);
- room.currentState.removeAllListeners(RoomStateEvent.Members);
- room.currentState.removeAllListeners(RoomStateEvent.NewMember);
- } */
-
- shouldAbortSync(error) {
- if (error.errcode === "M_UNKNOWN_TOKEN") {
- // The logout already happened, we just need to stop.
- _logger.logger.warn("Token no longer valid - assuming logout");
- this.stop();
- this.updateSyncState(_sync.SyncState.Error, {
- error
- });
- return true;
- }
- return false;
- }
- async processRoomData(client, room, roomData) {
- roomData = ensureNameEvent(client, room.roomId, roomData);
- const stateEvents = mapEvents(this.client, room.roomId, roomData.required_state);
- // Prevent events from being decrypted ahead of time
- // this helps large account to speed up faster
- // room::decryptCriticalEvent is in charge of decrypting all the events
- // required for a client to function properly
- let timelineEvents = mapEvents(this.client, room.roomId, roomData.timeline, false);
- const ephemeralEvents = []; // TODO this.mapSyncEventsFormat(joinObj.ephemeral);
-
- // TODO: handle threaded / beacon events
-
- if (roomData.initial) {
- // we should not know about any of these timeline entries if this is a genuinely new room.
- // If we do, then we've effectively done scrollback (e.g requesting timeline_limit: 1 for
- // this room, then timeline_limit: 50).
- const knownEvents = new Set();
- room.getLiveTimeline().getEvents().forEach(e => {
- knownEvents.add(e.getId());
- });
- // all unknown events BEFORE a known event must be scrollback e.g:
- // D E <-- what we know
- // A B C D E F <-- what we just received
- // means:
- // A B C <-- scrollback
- // D E <-- dupes
- // F <-- new event
- // We bucket events based on if we have seen a known event yet.
- const oldEvents = [];
- const newEvents = [];
- let seenKnownEvent = false;
- for (let i = timelineEvents.length - 1; i >= 0; i--) {
- const recvEvent = timelineEvents[i];
- if (knownEvents.has(recvEvent.getId())) {
- seenKnownEvent = true;
- continue; // don't include this event, it's a dupe
- }
-
- if (seenKnownEvent) {
- // old -> new
- oldEvents.push(recvEvent);
- } else {
- // old -> new
- newEvents.unshift(recvEvent);
- }
- }
- timelineEvents = newEvents;
- if (oldEvents.length > 0) {
- // old events are scrollback, insert them now
- room.addEventsToTimeline(oldEvents, true, room.getLiveTimeline(), roomData.prev_batch);
- }
- }
- const encrypted = this.client.isRoomEncrypted(room.roomId);
- // we do this first so it's correct when any of the events fire
- if (roomData.notification_count != null) {
- room.setUnreadNotificationCount(_room.NotificationCountType.Total, roomData.notification_count);
- }
- if (roomData.highlight_count != null) {
- // We track unread notifications ourselves in encrypted rooms, so don't
- // bother setting it here. We trust our calculations better than the
- // server's for this case, and therefore will assume that our non-zero
- // count is accurate.
- if (!encrypted || encrypted && room.getUnreadNotificationCount(_room.NotificationCountType.Highlight) <= 0) {
- room.setUnreadNotificationCount(_room.NotificationCountType.Highlight, roomData.highlight_count);
- }
- }
- if (Number.isInteger(roomData.invited_count)) {
- room.currentState.setInvitedMemberCount(roomData.invited_count);
- }
- if (Number.isInteger(roomData.joined_count)) {
- room.currentState.setJoinedMemberCount(roomData.joined_count);
- }
- if (roomData.invite_state) {
- const inviteStateEvents = mapEvents(this.client, room.roomId, roomData.invite_state);
- this.injectRoomEvents(room, inviteStateEvents);
- if (roomData.initial) {
- room.recalculate();
- this.client.store.storeRoom(room);
- this.client.emit(_client.ClientEvent.Room, room);
- }
- inviteStateEvents.forEach(e => {
- this.client.emit(_client.ClientEvent.Event, e);
- });
- room.updateMyMembership("invite");
- return;
- }
- if (roomData.initial) {
- var _roomData$prev_batch;
- // set the back-pagination token. Do this *before* adding any
- // events so that clients can start back-paginating.
- room.getLiveTimeline().setPaginationToken((_roomData$prev_batch = roomData.prev_batch) !== null && _roomData$prev_batch !== void 0 ? _roomData$prev_batch : null, _eventTimeline.EventTimeline.BACKWARDS);
- }
-
- /* TODO
- else if (roomData.limited) {
- let limited = true;
- // we've got a limited sync, so we *probably* have a gap in the
- // timeline, so should reset. But we might have been peeking or
- // paginating and already have some of the events, in which
- // case we just want to append any subsequent events to the end
- // of the existing timeline.
- //
- // This is particularly important in the case that we already have
- // *all* of the events in the timeline - in that case, if we reset
- // the timeline, we'll end up with an entirely empty timeline,
- // which we'll try to paginate but not get any new events (which
- // will stop us linking the empty timeline into the chain).
- //
- for (let i = timelineEvents.length - 1; i >= 0; i--) {
- const eventId = timelineEvents[i].getId();
- if (room.getTimelineForEvent(eventId)) {
- logger.debug("Already have event " + eventId + " in limited " +
- "sync - not resetting");
- limited = false;
- // we might still be missing some of the events before i;
- // we don't want to be adding them to the end of the
- // timeline because that would put them out of order.
- timelineEvents.splice(0, i);
- // XXX: there's a problem here if the skipped part of the
- // timeline modifies the state set in stateEvents, because
- // we'll end up using the state from stateEvents rather
- // than the later state from timelineEvents. We probably
- // need to wind stateEvents forward over the events we're
- // skipping.
- break;
- }
- }
- if (limited) {
- room.resetLiveTimeline(
- roomData.prev_batch,
- null, // TODO this.syncOpts.canResetEntireTimeline(room.roomId) ? null : syncEventData.oldSyncToken,
- );
- // We have to assume any gap in any timeline is
- // reason to stop incrementally tracking notifications and
- // reset the timeline.
- this.client.resetNotifTimelineSet();
- this.registerStateListeners(room);
- }
- } */
-
- this.injectRoomEvents(room, stateEvents, timelineEvents, roomData.num_live);
-
- // we deliberately don't add ephemeral events to the timeline
- room.addEphemeralEvents(ephemeralEvents);
-
- // local fields must be set before any async calls because call site assumes
- // synchronous execution prior to emitting SlidingSyncState.Complete
- room.updateMyMembership("join");
- room.recalculate();
- if (roomData.initial) {
- client.store.storeRoom(room);
- client.emit(_client.ClientEvent.Room, room);
- }
-
- // check if any timeline events should bing and add them to the notifEvents array:
- // we'll purge this once we've fully processed the sync response
- this.addNotifications(timelineEvents);
- const processRoomEvent = async e => {
- client.emit(_client.ClientEvent.Event, e);
- if (e.isState() && e.getType() == _event.EventType.RoomEncryption && this.syncOpts.cryptoCallbacks) {
- await this.syncOpts.cryptoCallbacks.onCryptoEvent(room, e);
- }
- };
- await utils.promiseMapSeries(stateEvents, processRoomEvent);
- await utils.promiseMapSeries(timelineEvents, processRoomEvent);
- ephemeralEvents.forEach(function (e) {
- client.emit(_client.ClientEvent.Event, e);
- });
-
- // Decrypt only the last message in all rooms to make sure we can generate a preview
- // And decrypt all events after the recorded read receipt to ensure an accurate
- // notification count
- room.decryptCriticalEvents();
- }
-
- /**
- * Injects events into a room's model.
- * @param stateEventList - A list of state events. This is the state
- * at the *START* of the timeline list if it is supplied.
- * @param timelineEventList - A list of timeline events. Lower index
- * is earlier in time. Higher index is later.
- * @param numLive - the number of events in timelineEventList which just happened,
- * supplied from the server.
- */
- injectRoomEvents(room, stateEventList, timelineEventList, numLive) {
- timelineEventList = timelineEventList || [];
- stateEventList = stateEventList || [];
- numLive = numLive || 0;
-
- // If there are no events in the timeline yet, initialise it with
- // the given state events
- const liveTimeline = room.getLiveTimeline();
- const timelineWasEmpty = liveTimeline.getEvents().length == 0;
- if (timelineWasEmpty) {
- // Passing these events into initialiseState will freeze them, so we need
- // to compute and cache the push actions for them now, otherwise sync dies
- // with an attempt to assign to read only property.
- // XXX: This is pretty horrible and is assuming all sorts of behaviour from
- // these functions that it shouldn't be. We should probably either store the
- // push actions cache elsewhere so we can freeze MatrixEvents, or otherwise
- // find some solution where MatrixEvents are immutable but allow for a cache
- // field.
- for (const ev of stateEventList) {
- this.client.getPushActionsForEvent(ev);
- }
- liveTimeline.initialiseState(stateEventList);
- }
-
- // If the timeline wasn't empty, we process the state events here: they're
- // defined as updates to the state before the start of the timeline, so this
- // starts to roll the state forward.
- // XXX: That's what we *should* do, but this can happen if we were previously
- // peeking in a room, in which case we obviously do *not* want to add the
- // state events here onto the end of the timeline. Historically, the js-sdk
- // has just set these new state events on the old and new state. This seems
- // very wrong because there could be events in the timeline that diverge the
- // state, in which case this is going to leave things out of sync. However,
- // for now I think it;s best to behave the same as the code has done previously.
- if (!timelineWasEmpty) {
- // XXX: As above, don't do this...
- //room.addLiveEvents(stateEventList || []);
- // Do this instead...
- room.oldState.setStateEvents(stateEventList);
- room.currentState.setStateEvents(stateEventList);
- }
-
- // the timeline is broken into 'live' events which just happened and normal timeline events
- // which are still to be appended to the end of the live timeline but happened a while ago.
- // The live events are marked as fromCache=false to ensure that downstream components know
- // this is a live event, not historical (from a remote server cache).
-
- let liveTimelineEvents = [];
- if (numLive > 0) {
- // last numLive events are live
- liveTimelineEvents = timelineEventList.slice(-1 * numLive);
- // everything else is not live
- timelineEventList = timelineEventList.slice(0, -1 * liveTimelineEvents.length);
- }
-
- // execute the timeline events. This will continue to diverge the current state
- // if the timeline has any state events in it.
- // This also needs to be done before running push rules on the events as they need
- // to be decorated with sender etc.
- room.addLiveEvents(timelineEventList, {
- fromCache: true
- });
- if (liveTimelineEvents.length > 0) {
- room.addLiveEvents(liveTimelineEvents, {
- fromCache: false
- });
- }
- room.recalculate();
-
- // resolve invites now we have set the latest state
- this.resolveInvites(room);
- }
- resolveInvites(room) {
- if (!room || !this.opts.resolveInvitesToProfiles) {
- return;
- }
- const client = this.client;
- // For each invited room member we want to give them a displayname/avatar url
- // if they have one (the m.room.member invites don't contain this).
- room.getMembersWithMembership("invite").forEach(function (member) {
- if (member.requestedProfileInfo) return;
- member.requestedProfileInfo = true;
- // try to get a cached copy first.
- const user = client.getUser(member.userId);
- let promise;
- if (user) {
- promise = Promise.resolve({
- avatar_url: user.avatarUrl,
- displayname: user.displayName
- });
- } else {
- promise = client.getProfileInfo(member.userId);
- }
- promise.then(function (info) {
- // slightly naughty by doctoring the invite event but this means all
- // the code paths remain the same between invite/join display name stuff
- // which is a worthy trade-off for some minor pollution.
- const inviteEvent = member.events.member;
- if (inviteEvent.getContent().membership !== "invite") {
- // between resolving and now they have since joined, so don't clobber
- return;
- }
- inviteEvent.getContent().avatar_url = info.avatar_url;
- inviteEvent.getContent().displayname = info.displayname;
- // fire listeners
- member.setMembershipEvent(inviteEvent, room.currentState);
- }, function (_err) {
- // OH WELL.
- });
- });
- }
- retryImmediately() {
- return true;
- }
-
- /**
- * Main entry point. Blocks until stop() is called.
- */
- async sync() {
- _logger.logger.debug("Sliding sync init loop");
-
- // 1) We need to get push rules so we can check if events should bing as we get
- // them from /sync.
- while (!this.client.isGuest()) {
- try {
- _logger.logger.debug("Getting push rules...");
- const result = await this.client.getPushRules();
- _logger.logger.debug("Got push rules");
- this.client.pushRules = result;
- break;
- } catch (err) {
- _logger.logger.error("Getting push rules failed", err);
- if (this.shouldAbortSync(err)) {
- return;
- }
- }
- }
-
- // start syncing
- await this.slidingSync.start();
- }
-
- /**
- * Stops the sync object from syncing.
- */
- stop() {
- _logger.logger.debug("SyncApi.stop");
- this.slidingSync.stop();
- }
-
- /**
- * Sets the sync state and emits an event to say so
- * @param newState - The new state string
- * @param data - Object of additional data to emit in the event
- */
- updateSyncState(newState, data) {
- const old = this.syncState;
- this.syncState = newState;
- this.syncStateData = data;
- this.client.emit(_client.ClientEvent.Sync, this.syncState, old, data);
- }
-
- /**
- * Takes a list of timelineEvents and adds and adds to notifEvents
- * as appropriate.
- * This must be called after the room the events belong to has been stored.
- *
- * @param timelineEventList - A list of timeline events. Lower index
- * is earlier in time. Higher index is later.
- */
- addNotifications(timelineEventList) {
- // gather our notifications into this.notifEvents
- if (!this.client.getNotifTimelineSet()) {
- return;
- }
- for (const timelineEvent of timelineEventList) {
- const pushActions = this.client.getPushActionsForEvent(timelineEvent);
- if (pushActions && pushActions.notify && pushActions.tweaks && pushActions.tweaks.highlight) {
- this.notifEvents.push(timelineEvent);
- }
- }
- }
-
- /**
- * Purge any events in the notifEvents array. Used after a /sync has been complete.
- * This should not be called at a per-room scope (e.g in onRoomData) because otherwise the ordering
- * will be messed up e.g room A gets a bing, room B gets a newer bing, but both in the same /sync
- * response. If we purge at a per-room scope then we could process room B before room A leading to
- * room B appearing earlier in the notifications timeline, even though it has the higher origin_server_ts.
- */
- purgeNotifications() {
- this.notifEvents.sort(function (a, b) {
- return a.getTs() - b.getTs();
- });
- this.notifEvents.forEach(event => {
- var _this$client$getNotif;
- (_this$client$getNotif = this.client.getNotifTimelineSet()) === null || _this$client$getNotif === void 0 ? void 0 : _this$client$getNotif.addLiveEvent(event);
- });
- this.notifEvents = [];
- }
-}
-exports.SlidingSyncSdk = SlidingSyncSdk;
-function ensureNameEvent(client, roomId, roomData) {
- // make sure m.room.name is in required_state if there is a name, replacing anything previously
- // there if need be. This ensures clients transparently 'calculate' the right room name. Native
- // sliding sync clients should just read the "name" field.
- if (!roomData.name) {
- return roomData;
- }
- for (const stateEvent of roomData.required_state) {
- if (stateEvent.type === _event.EventType.RoomName && stateEvent.state_key === "") {
- stateEvent.content = {
- name: roomData.name
- };
- return roomData;
- }
- }
- roomData.required_state.push({
- event_id: "$fake-sliding-sync-name-event-" + roomId,
- state_key: "",
- type: _event.EventType.RoomName,
- content: {
- name: roomData.name
- },
- sender: client.getUserId(),
- origin_server_ts: new Date().getTime()
- });
- return roomData;
-}
-// Helper functions which set up JS SDK structs are below and are identical to the sync v2 counterparts,
-// just outside the class.
-function mapEvents(client, roomId, events, decrypt = true) {
- const mapper = client.getEventMapper({
- decrypt
- });
- return events.map(function (e) {
- e.room_id = roomId;
- return mapper(e);
- });
-}
-function processEphemeralEvents(client, roomId, ephEvents) {
- const ephemeralEvents = mapEvents(client, roomId, ephEvents);
- const room = client.getRoom(roomId);
- if (!room) {
- _logger.logger.warn("got ephemeral events for room but room doesn't exist on client:", roomId);
- return;
- }
- room.addEphemeralEvents(ephemeralEvents);
- ephemeralEvents.forEach(e => {
- client.emit(_client.ClientEvent.Event, e);
- });
-}
-//# sourceMappingURL=sliding-sync-sdk.js.map \ No newline at end of file