1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-12-08 19:08:34 +03:00

More safely set the push actions for an encrypted event

This commit is contained in:
Travis Ralston
2019-03-05 14:04:39 -07:00
parent 829cd05cba
commit 37f106d4af
3 changed files with 68 additions and 20 deletions

View File

@@ -228,6 +228,30 @@ function MatrixClient(opts) {
this._serverSupportsLazyLoading = null;
this._cachedCapabilities = null; // { capabilities: {}, lastUpdated: timestamp }
// The SDK doesn't really provide a clean way for events to recalculate the push
// actions for themselves, so we have to kinda help them out when they are encrypted.
// We do this so that push rules are correctly executed on events in their decrypted
// state, such as highlights when the user's name is mentioned.
this.on("Event.decrypted", (event) => {
const oldActions = event.getPushActions();
const actions = this._pushProcessor.actionsForEvent(event);
event.setPushActions(actions); // Might as well while we're here
// Ensure the unread counts are kept up to date if the event is encrypted
const oldHighlight = oldActions && oldActions.tweaks
? !!oldActions.tweaks.highlight : false;
const newHighlight = actions && actions.tweaks
? !!actions.tweaks.highlight : false;
if (oldHighlight !== newHighlight) {
const room = this.getRoom(event.getRoomId());
if (room && !room.hasUserReadEvent(this.getUserId(), event.getId())) {
const current = room.getUnreadNotificationCount("highlight");
const newCount = newHighlight ? current + 1 : current - 1;
room.setUnreadNotificationCount("highlight", newCount);
}
}
});
}
utils.inherits(MatrixClient, EventEmitter);
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype);
@@ -2299,28 +2323,11 @@ function _membershipChange(client, roomId, userId, membership, reason, callback)
* Obtain a dict of actions which should be performed for this event according
* to the push rules for this user. Caches the dict on the event.
* @param {MatrixEvent} event The event to get push actions for.
* @param {boolean} ignoreCache True to skip the cache and recalculate the push rules.
* @return {module:pushprocessor~PushAction} A dict of actions to perform.
*/
MatrixClient.prototype.getPushActionsForEvent = function(event, ignoreCache = false) {
if (!event.getPushActions() || ignoreCache) {
const oldActions = event.getPushActions();
const actions = this._pushProcessor.actionsForEvent(event);
event.setPushActions(actions);
// Ensure the unread counts are kept up to date if the event is encrypted
const oldHighlight = oldActions && oldActions.tweaks
? !!oldActions.tweaks.highlight : false;
const newHighlight = actions && actions.tweaks
? !!actions.tweaks.highlight : false;
if (oldHighlight !== newHighlight && event.isEncrypted()) {
const room = this.getRoom(event.getRoomId());
if (room) {
const current = room.getUnreadNotificationCount("highlight");
const newCount = newHighlight ? current + 1 : current - 1;
room.setUnreadNotificationCount("highlight", newCount);
}
}
MatrixClient.prototype.getPushActionsForEvent = function(event) {
if (!event.getPushActions()) {
event.setPushActions(this._pushProcessor.actionsForEvent(event));
}
return event.getPushActions();
};

View File

@@ -471,6 +471,14 @@ utils.extend(module.exports.MatrixEvent.prototype, {
this._retryDecryption = false;
this._setClearData(res);
// Before we emit the event, clear the push actions so that they can be recalculated
// by relevant code. We do this because the clear event has now changed, making it
// so that existing rules can be re-run over the applicable properties. Stuff like
// highlighting when the user's name is mentioned rely on this happening. We also want
// to set the push actions before emitting so that any notification listeners don't
// pick up the wrong contents.
this.setPushActions(null);
this.emit("Event.decrypted", this, err);
return;

View File

@@ -1424,6 +1424,39 @@ Room.prototype.getEventReadUpTo = function(userId, ignoreSynthesized) {
return receipts["m.read"][userId].eventId;
};
/**
* Determines if the given user has read a particular event ID with the known
* history of the room. This is not a definitive check as it relies only on
* what is available to the room at the time of execution.
* @param {String} userId The user ID to check the read state of.
* @param {String} eventId The event ID to check if the user read.
*/
Room.prototype.hasUserReadEvent = function(userId, eventId) {
const readUpToId = this.getEventReadUpTo(userId, false);
if (readUpToId === eventId) return true;
if (this.timeline.length
&& this.timeline[this.timeline.length - 1].getSender()
&& this.timeline[this.timeline.length - 1].getSender() === userId) {
// It doesn't matter where the event is in the timeline, the user has read
// it because they've sent the latest event.
return true;
}
for (let i = this.timeline.length - 1; i >= 0; --i) {
const ev = this.timeline[i];
// If we encounter the target event first, the user hasn't read it
// however if we encounter the readUpToId first then the user has read
// it. These rules apply because we're iterating bottom-up.
if (ev.getId() === eventId) return false;
if (ev.getId() === readUpToId) return true;
}
// We don't know if the user has read it, so assume not.
return false;
};
/**
* Get a list of receipts for the given event.
* @param {MatrixEvent} event the event to get receipts for