Skip to content

Commit 77270fa

Browse files
authored
Merge pull request #851 from matrix-org/travis/e2e-notifs
Calculate encrypted notification counts
2 parents 8beb836 + 6198943 commit 77270fa

File tree

4 files changed

+83
-1
lines changed

4 files changed

+83
-1
lines changed

src/client.js

+26
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,32 @@ function MatrixClient(opts) {
228228
this._serverSupportsLazyLoading = null;
229229

230230
this._cachedCapabilities = null; // { capabilities: {}, lastUpdated: timestamp }
231+
232+
// The SDK doesn't really provide a clean way for events to recalculate the push
233+
// actions for themselves, so we have to kinda help them out when they are encrypted.
234+
// We do this so that push rules are correctly executed on events in their decrypted
235+
// state, such as highlights when the user's name is mentioned.
236+
this.on("Event.decrypted", (event) => {
237+
const oldActions = event.getPushActions();
238+
const actions = this._pushProcessor.actionsForEvent(event);
239+
event.setPushActions(actions); // Might as well while we're here
240+
241+
// Ensure the unread counts are kept up to date if the event is encrypted
242+
const oldHighlight = oldActions && oldActions.tweaks
243+
? !!oldActions.tweaks.highlight : false;
244+
const newHighlight = actions && actions.tweaks
245+
? !!actions.tweaks.highlight : false;
246+
if (oldHighlight !== newHighlight) {
247+
const room = this.getRoom(event.getRoomId());
248+
// TODO: Handle mentions received while the client is offline
249+
// See also https://github.com/vector-im/riot-web/issues/9069
250+
if (room && !room.hasUserReadEvent(this.getUserId(), event.getId())) {
251+
const current = room.getUnreadNotificationCount("highlight");
252+
const newCount = newHighlight ? current + 1 : current - 1;
253+
room.setUnreadNotificationCount("highlight", newCount);
254+
}
255+
}
256+
});
231257
}
232258
utils.inherits(MatrixClient, EventEmitter);
233259
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype);

src/models/event.js

+19
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,14 @@ utils.extend(module.exports.MatrixEvent.prototype, {
497497
this._retryDecryption = false;
498498
this._setClearData(res);
499499

500+
// Before we emit the event, clear the push actions so that they can be recalculated
501+
// by relevant code. We do this because the clear event has now changed, making it
502+
// so that existing rules can be re-run over the applicable properties. Stuff like
503+
// highlighting when the user's name is mentioned rely on this happening. We also want
504+
// to set the push actions before emitting so that any notification listeners don't
505+
// pick up the wrong contents.
506+
this.setPushActions(null);
507+
500508
this.emit("Event.decrypted", this, err);
501509

502510
return;
@@ -537,6 +545,17 @@ utils.extend(module.exports.MatrixEvent.prototype, {
537545
decryptionResult.forwardingCurve25519KeyChain || [];
538546
},
539547

548+
/**
549+
* Gets the cleartext content for this event. If the event is not encrypted,
550+
* or encryption has not been completed, this will return null.
551+
*
552+
* @returns {Object} The cleartext (decrypted) content for the event
553+
*/
554+
getClearContent: function() {
555+
const ev = this._clearEvent;
556+
return ev && ev.content ? ev.content : null;
557+
},
558+
540559
/**
541560
* Check if the event is encrypted.
542561
* @return {boolean} True if this event is encrypted.

src/models/room.js

+34
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,40 @@ Room.prototype.getEventReadUpTo = function(userId, ignoreSynthesized) {
14241424
return receipts["m.read"][userId].eventId;
14251425
};
14261426

1427+
/**
1428+
* Determines if the given user has read a particular event ID with the known
1429+
* history of the room. This is not a definitive check as it relies only on
1430+
* what is available to the room at the time of execution.
1431+
* @param {String} userId The user ID to check the read state of.
1432+
* @param {String} eventId The event ID to check if the user read.
1433+
* @returns {Boolean} True if the user has read the event, false otherwise.
1434+
*/
1435+
Room.prototype.hasUserReadEvent = function(userId, eventId) {
1436+
const readUpToId = this.getEventReadUpTo(userId, false);
1437+
if (readUpToId === eventId) return true;
1438+
1439+
if (this.timeline.length
1440+
&& this.timeline[this.timeline.length - 1].getSender()
1441+
&& this.timeline[this.timeline.length - 1].getSender() === userId) {
1442+
// It doesn't matter where the event is in the timeline, the user has read
1443+
// it because they've sent the latest event.
1444+
return true;
1445+
}
1446+
1447+
for (let i = this.timeline.length - 1; i >= 0; --i) {
1448+
const ev = this.timeline[i];
1449+
1450+
// If we encounter the target event first, the user hasn't read it
1451+
// however if we encounter the readUpToId first then the user has read
1452+
// it. These rules apply because we're iterating bottom-up.
1453+
if (ev.getId() === eventId) return false;
1454+
if (ev.getId() === readUpToId) return true;
1455+
}
1456+
1457+
// We don't know if the user has read it, so assume not.
1458+
return false;
1459+
};
1460+
14271461
/**
14281462
* Get a list of receipts for the given event.
14291463
* @param {MatrixEvent} event the event to get receipts for

src/pushprocessor.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,10 @@ function PushProcessor(client) {
184184
};
185185

186186
const eventFulfillsDisplayNameCondition = function(cond, ev) {
187-
const content = ev.getContent();
187+
let content = ev.getContent();
188+
if (ev.isEncrypted() && ev.getClearContent()) {
189+
content = ev.getClearContent();
190+
}
188191
if (!content || !content.body || typeof content.body != 'string') {
189192
return false;
190193
}

0 commit comments

Comments
 (0)