Prioritise and reduce the amount of events decrypted on application startup (#5980)
This commit is contained in:
commit
91133ed35b
4 changed files with 65 additions and 24 deletions
|
@ -908,6 +908,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
let presentedId = roomInfo.room_alias || roomInfo.room_id;
|
let presentedId = roomInfo.room_alias || roomInfo.room_id;
|
||||||
const room = MatrixClientPeg.get().getRoom(roomInfo.room_id);
|
const room = MatrixClientPeg.get().getRoom(roomInfo.room_id);
|
||||||
if (room) {
|
if (room) {
|
||||||
|
// Not all timeline events are decrypted ahead of time anymore
|
||||||
|
// Only the critical ones for a typical UI are
|
||||||
|
// This will start the decryption process for all events when a
|
||||||
|
// user views a room
|
||||||
|
room.decryptAllEvents();
|
||||||
const theAlias = Rooms.getDisplayAliasForRoom(room);
|
const theAlias = Rooms.getDisplayAliasForRoom(room);
|
||||||
if (theAlias) {
|
if (theAlias) {
|
||||||
presentedId = theAlias;
|
presentedId = theAlias;
|
||||||
|
|
|
@ -38,6 +38,7 @@ import {haveTileForEvent} from "../views/rooms/EventTile";
|
||||||
import {UIFeature} from "../../settings/UIFeature";
|
import {UIFeature} from "../../settings/UIFeature";
|
||||||
import {objectHasDiff} from "../../utils/objects";
|
import {objectHasDiff} from "../../utils/objects";
|
||||||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||||
|
import { arrayFastClone } from "../../utils/arrays";
|
||||||
|
|
||||||
const PAGINATE_SIZE = 20;
|
const PAGINATE_SIZE = 20;
|
||||||
const INITIAL_SIZE = 20;
|
const INITIAL_SIZE = 20;
|
||||||
|
@ -1141,6 +1142,18 @@ class TimelinePanel extends React.Component {
|
||||||
// get the list of events from the timeline window and the pending event list
|
// get the list of events from the timeline window and the pending event list
|
||||||
_getEvents() {
|
_getEvents() {
|
||||||
const events = this._timelineWindow.getEvents();
|
const events = this._timelineWindow.getEvents();
|
||||||
|
|
||||||
|
// `arrayFastClone` performs a shallow copy of the array
|
||||||
|
// we want the last event to be decrypted first but displayed last
|
||||||
|
// `reverse` is destructive and unfortunately mutates the "events" array
|
||||||
|
arrayFastClone(events)
|
||||||
|
.reverse()
|
||||||
|
.forEach(event => {
|
||||||
|
if (event.shouldAttemptDecryption()) {
|
||||||
|
event.attemptDecryption(MatrixClientPeg.get()._crypto);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const firstVisibleEventIndex = this._checkForPreJoinUISI(events);
|
const firstVisibleEventIndex = this._checkForPreJoinUISI(events);
|
||||||
|
|
||||||
// Hold onto the live events separately. The read receipt and read marker
|
// Hold onto the live events separately. The read receipt and read marker
|
||||||
|
|
|
@ -38,7 +38,6 @@ export default class EventIndex extends EventEmitter {
|
||||||
this._eventsPerCrawl = 100;
|
this._eventsPerCrawl = 100;
|
||||||
this._crawler = null;
|
this._crawler = null;
|
||||||
this._currentCheckpoint = null;
|
this._currentCheckpoint = null;
|
||||||
this.liveEventsForIndex = new Set();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
@ -188,16 +187,12 @@ export default class EventIndex extends EventEmitter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the event is not yet decrypted mark it for the
|
|
||||||
// Event.decrypted callback.
|
|
||||||
if (ev.isBeingDecrypted()) {
|
if (ev.isBeingDecrypted()) {
|
||||||
const eventId = ev.getId();
|
// XXX: Private member access
|
||||||
this.liveEventsForIndex.add(eventId);
|
await ev._decryptionPromise;
|
||||||
} else {
|
|
||||||
// If the event is decrypted or is unencrypted add it to the
|
|
||||||
// index now.
|
|
||||||
await this.addLiveEventToIndex(ev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.addLiveEventToIndex(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRoomStateEvent = async (ev, state) => {
|
onRoomStateEvent = async (ev, state) => {
|
||||||
|
@ -216,10 +211,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
* listener, if so queues it up to be added to the index.
|
* listener, if so queues it up to be added to the index.
|
||||||
*/
|
*/
|
||||||
onEventDecrypted = async (ev, err) => {
|
onEventDecrypted = async (ev, err) => {
|
||||||
const eventId = ev.getId();
|
|
||||||
|
|
||||||
// If the event isn't in our live event set, ignore it.
|
// If the event isn't in our live event set, ignore it.
|
||||||
if (!this.liveEventsForIndex.delete(eventId)) return;
|
|
||||||
if (err) return;
|
if (err) return;
|
||||||
await this.addLiveEventToIndex(ev);
|
await this.addLiveEventToIndex(ev);
|
||||||
}
|
}
|
||||||
|
@ -523,16 +515,21 @@ export default class EventIndex extends EventEmitter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const decryptionPromises = [];
|
const decryptionPromises = matrixEvents
|
||||||
|
.filter(event => event.isEncrypted())
|
||||||
matrixEvents.forEach(ev => {
|
.map(event => {
|
||||||
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) {
|
if (event.shouldAttemptDecryption()) {
|
||||||
|
return event.attemptDecryption(client._crypto, {
|
||||||
|
isRetry: true,
|
||||||
|
emit: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
// TODO the decryption promise is a private property, this
|
// TODO the decryption promise is a private property, this
|
||||||
// should either be made public or we should convert the
|
// should either be made public or we should convert the
|
||||||
// event that gets fired when decryption is done into a
|
// event that gets fired when decryption is done into a
|
||||||
// promise using the once event emitter method:
|
// promise using the once event emitter method:
|
||||||
// https://nodejs.org/api/events.html#events_events_once_emitter_name
|
// https://nodejs.org/api/events.html#events_events_once_emitter_name
|
||||||
decryptionPromises.push(ev._decryptionPromise);
|
return event._decryptionPromise;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,14 @@ limitations under the License.
|
||||||
|
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { ActionPayload } from "../dispatcher/payloads";
|
import { ActionPayload } from "../dispatcher/payloads";
|
||||||
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||||
import { arrayHasDiff } from "../utils/arrays";
|
import { arrayHasDiff } from "../utils/arrays";
|
||||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||||
import { SettingLevel } from "../settings/SettingLevel";
|
import { SettingLevel } from "../settings/SettingLevel";
|
||||||
|
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
|
|
||||||
const MAX_ROOMS = 20; // arbitrary
|
const MAX_ROOMS = 20; // arbitrary
|
||||||
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up
|
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up
|
||||||
|
@ -59,6 +61,30 @@ export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
|
||||||
return this.matrixClient && this.matrixClient.getVisibleRooms().length >= 20;
|
return this.matrixClient && this.matrixClient.getVisibleRooms().length >= 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
const prevRoomCount = (prevState.rooms?.length || 0);
|
||||||
|
const currentRoomCount = (this.state.rooms?.length || 0)
|
||||||
|
|
||||||
|
// Only decrypting the breadcrumb rooms events on app initialisation
|
||||||
|
// when room count transitions from 0 to the number of rooms it contains
|
||||||
|
if (prevRoomCount === 0 && currentRoomCount > prevRoomCount) {
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
// Rooms in the breadcrumb have a good chance to be interacted with
|
||||||
|
// again by a user. Decrypting the messages ahead of time will help
|
||||||
|
// reduce content shift on first render
|
||||||
|
this.state.rooms?.forEach(async room => {
|
||||||
|
const [cryptoEvent] = room.currentState.getStateEvents(EventType.RoomEncryption);
|
||||||
|
if (cryptoEvent) {
|
||||||
|
if (!client.isRoomEncrypted(room.roomId)) {
|
||||||
|
// XXX: Private member access
|
||||||
|
await client._crypto.onCryptoEvent(cryptoEvent);
|
||||||
|
}
|
||||||
|
room?.decryptAllEvents();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async onAction(payload: ActionPayload) {
|
protected async onAction(payload: ActionPayload) {
|
||||||
if (!this.matrixClient) return;
|
if (!this.matrixClient) return;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue