Merge pull request #107 from matrix-org/unread_sync
Use read receipts to calculate unread room status
This commit is contained in:
commit
97d42b3ad7
3 changed files with 64 additions and 69 deletions
62
src/Unread.js
Normal file
62
src/Unread.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
var MatrixClientPeg = require('./MatrixClientPeg');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Returns true iff this event arriving in a room should affect the room's
|
||||
* count of unread messages
|
||||
*/
|
||||
eventTriggersUnreadCount: function(ev) {
|
||||
if (ev.getType() == "m.room.member") {
|
||||
return false;
|
||||
} else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
doesRoomHaveUnreadMessages: function(room) {
|
||||
var readUpToId = room.getEventReadUpTo(MatrixClientPeg.get().credentials.userId);
|
||||
// this just looks at whatever history we have, which if we've only just started
|
||||
// up probably won't be very much, so if the last couple of events are ones that
|
||||
// don't count, we don't know if there are any events that do count between where
|
||||
// we have and the read receipt. We could fetch more history to try & find out,
|
||||
// but currently we just guess.
|
||||
|
||||
// Loop through messages, starting with the most recent...
|
||||
for (var i = room.timeline.length - 1; i >= 0; --i) {
|
||||
var ev = room.timeline[i];
|
||||
|
||||
if (ev.getId() == readUpToId) {
|
||||
// If we've read up to this event, there's nothing more recents
|
||||
// that counts and we can stop looking because the user's read
|
||||
// this and everything before.
|
||||
return false;
|
||||
} else if (this.eventTriggersUnreadCount(ev)) {
|
||||
// We've found a message that counts before we hit
|
||||
// the read marker, so this room is definitely unread.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If we got here, we didn't find a message that counted but didn't
|
||||
// find the read marker either, so we guess and say that the room
|
||||
// is unread on the theory that false positives are better than
|
||||
// false negatives here.
|
||||
return true;
|
||||
}
|
||||
};
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.exports = {
|
||||
/**
|
||||
* Returns true iff this event arriving in a room should affect the room's
|
||||
* count of unread messages
|
||||
*/
|
||||
eventTriggersUnreadCount: function(ev) {
|
||||
if (ev.getType() == "m.room.member") {
|
||||
return false;
|
||||
} else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
|
@ -21,7 +21,7 @@ var GeminiScrollbar = require('react-gemini-scrollbar');
|
|||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
var CallHandler = require('../../../CallHandler');
|
||||
var RoomListSorter = require("../../../RoomListSorter");
|
||||
var UnreadStatus = require('../../../UnreadStatus');
|
||||
var Unread = require('../../../Unread');
|
||||
var dis = require("../../../dispatcher");
|
||||
var sdk = require('../../../index');
|
||||
|
||||
|
@ -38,7 +38,6 @@ module.exports = React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
activityMap: null,
|
||||
isLoadingLeftRooms: false,
|
||||
lists: {},
|
||||
incomingCall: null,
|
||||
|
@ -57,7 +56,6 @@ module.exports = React.createClass({
|
|||
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||
|
||||
var s = this.getRoomLists();
|
||||
s.activityMap = {};
|
||||
this.setState(s);
|
||||
},
|
||||
|
||||
|
@ -100,13 +98,6 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
this.state.activityMap[newProps.selectedRoom] = undefined;
|
||||
this.setState({
|
||||
activityMap: this.state.activityMap
|
||||
});
|
||||
},
|
||||
|
||||
onRoom: function(room) {
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
@ -132,29 +123,7 @@ module.exports = React.createClass({
|
|||
|
||||
onRoomTimeline: function(ev, room, toStartOfTimeline) {
|
||||
if (toStartOfTimeline) return;
|
||||
|
||||
var hl = 0;
|
||||
if (
|
||||
room.roomId != this.props.selectedRoom &&
|
||||
ev.getSender() != MatrixClientPeg.get().credentials.userId)
|
||||
{
|
||||
if (UnreadStatus.eventTriggersUnreadCount(ev)) {
|
||||
hl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var newState = this.getRoomLists();
|
||||
if (hl > 0) {
|
||||
// obviously this won't deep copy but this shouldn't be necessary
|
||||
var amap = this.state.activityMap;
|
||||
amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl);
|
||||
|
||||
newState.activityMap = amap;
|
||||
|
||||
}
|
||||
// still want to update the list even if the highlight status
|
||||
// hasn't changed because the ordering may have
|
||||
this.setState(newState);
|
||||
this.refreshRoomList();
|
||||
},
|
||||
|
||||
onRoomReceipt: function(receiptEvent, room) {
|
||||
|
@ -373,7 +342,6 @@ module.exports = React.createClass({
|
|||
label="Invites"
|
||||
editable={ false }
|
||||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
@ -384,7 +352,6 @@ module.exports = React.createClass({
|
|||
verb="favourite"
|
||||
editable={ true }
|
||||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
@ -394,7 +361,6 @@ module.exports = React.createClass({
|
|||
editable={ true }
|
||||
verb="restore"
|
||||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
@ -408,7 +374,6 @@ module.exports = React.createClass({
|
|||
verb={ "tag as " + tagName }
|
||||
editable={ true }
|
||||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
@ -422,7 +387,6 @@ module.exports = React.createClass({
|
|||
verb="demote"
|
||||
editable={ true }
|
||||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
@ -431,7 +395,6 @@ module.exports = React.createClass({
|
|||
label="Historical"
|
||||
editable={ false }
|
||||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed }
|
||||
alwaysShowHeader={ true }
|
||||
|
|
Loading…
Reference in a new issue