Merge branch 'develop' into experimental
This commit is contained in:
commit
c19b593f5c
7 changed files with 154 additions and 19 deletions
|
@ -199,25 +199,12 @@ module.exports = function (config) {
|
|||
|
||||
'matrix-react-sdk': path.resolve('test/skinned-sdk.js'),
|
||||
'sinon': 'sinon/pkg/sinon.js',
|
||||
|
||||
// To make webpack happy
|
||||
// Related: https://github.com/request/request/issues/1529
|
||||
// (there's no mock available for fs, so we fake a mock by using
|
||||
// an in-memory version of fs)
|
||||
"fs": "memfs",
|
||||
},
|
||||
modules: [
|
||||
path.resolve('./test'),
|
||||
"node_modules"
|
||||
],
|
||||
},
|
||||
node: {
|
||||
// Because webpack is made of fail
|
||||
// https://github.com/request/request/issues/1529
|
||||
// Note: 'mock' is the new 'empty'
|
||||
net: 'mock',
|
||||
tls: 'mock'
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
externals: {
|
||||
// Don't try to bundle electron: leave it as a commonjs dependency
|
||||
|
|
|
@ -76,7 +76,6 @@
|
|||
"lodash": "^4.13.1",
|
||||
"lolex": "2.3.2",
|
||||
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
|
||||
"memfs": "^2.10.1",
|
||||
"optimist": "^0.6.1",
|
||||
"pako": "^1.0.5",
|
||||
"prop-types": "^15.5.8",
|
||||
|
|
|
@ -62,6 +62,35 @@ function createAccountDataAction(matrixClient, accountDataEvent) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef RoomAccountDataAction
|
||||
* @type {Object}
|
||||
* @property {string} action 'MatrixActions.Room.accountData'.
|
||||
* @property {MatrixEvent} event the MatrixEvent that triggered the dispatch.
|
||||
* @property {string} event_type the type of the MatrixEvent, e.g. "m.direct".
|
||||
* @property {Object} event_content the content of the MatrixEvent.
|
||||
* @property {Room} room the room where the account data was changed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a MatrixActions.Room.accountData action that represents a MatrixClient `Room.accountData`
|
||||
* matrix event.
|
||||
*
|
||||
* @param {MatrixClient} matrixClient the matrix client.
|
||||
* @param {MatrixEvent} accountDataEvent the account data event.
|
||||
* @param {Room} room the room where account data was changed
|
||||
* @returns {RoomAccountDataAction} an action of type MatrixActions.Room.accountData.
|
||||
*/
|
||||
function createRoomAccountDataAction(matrixClient, accountDataEvent, room) {
|
||||
return {
|
||||
action: 'MatrixActions.Room.accountData',
|
||||
event: accountDataEvent,
|
||||
event_type: accountDataEvent.getType(),
|
||||
event_content: accountDataEvent.getContent(),
|
||||
room: room,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef RoomAction
|
||||
* @type {Object}
|
||||
|
@ -201,6 +230,7 @@ export default {
|
|||
start(matrixClient) {
|
||||
this._addMatrixClientListener(matrixClient, 'sync', createSyncAction);
|
||||
this._addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.accountData', createRoomAccountDataAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room', createRoomAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.tags', createRoomTagsAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.timeline', createRoomTimelineAction);
|
||||
|
|
|
@ -82,6 +82,8 @@ const SIMPLE_SETTINGS = [
|
|||
{ id: "TagPanel.disableTagPanel" },
|
||||
{ id: "enableWidgetScreenshots" },
|
||||
{ id: "RoomSubList.showEmpty" },
|
||||
{ id: "pinMentionedRooms" },
|
||||
{ id: "pinUnreadRooms" },
|
||||
{ id: "showDeveloperTools" },
|
||||
];
|
||||
|
||||
|
|
|
@ -249,6 +249,8 @@
|
|||
"Enable URL previews for this room (only affects you)": "Enable URL previews for this room (only affects you)",
|
||||
"Enable URL previews by default for participants in this room": "Enable URL previews by default for participants in this room",
|
||||
"Room Colour": "Room Colour",
|
||||
"Pin unread rooms to the top of the room list": "Pin unread rooms to the top of the room list",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Pin rooms I'm mentioned in to the top of the room list",
|
||||
"Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets",
|
||||
"Show empty room list headings": "Show empty room list headings",
|
||||
"Collecting app version information": "Collecting app version information",
|
||||
|
|
|
@ -276,6 +276,16 @@ export const SETTINGS = {
|
|||
default: true,
|
||||
controller: new AudioNotificationsEnabledController(),
|
||||
},
|
||||
"pinMentionedRooms": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("Pin rooms I'm mentioned in to the top of the room list"),
|
||||
default: false,
|
||||
},
|
||||
"pinUnreadRooms": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("Pin unread rooms to the top of the room list"),
|
||||
default: false,
|
||||
},
|
||||
"enableWidgetScreenshots": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td('Enable widget screenshots on supported widgets'),
|
||||
|
|
|
@ -17,6 +17,7 @@ import {Store} from 'flux/utils';
|
|||
import dis from '../dispatcher';
|
||||
import DMRoomMap from '../utils/DMRoomMap';
|
||||
import Unread from '../Unread';
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
|
||||
/**
|
||||
* A class for storing application state for categorising rooms in
|
||||
|
@ -53,6 +54,24 @@ class RoomListStore extends Store {
|
|||
"im.vector.fake.archived": [],
|
||||
},
|
||||
ready: false,
|
||||
|
||||
// The room cache stores a mapping of roomId to cache record.
|
||||
// Each cache record is a key/value pair for various bits of
|
||||
// data used to sort the room list. Currently this stores the
|
||||
// following bits of informations:
|
||||
// "timestamp": number, The timestamp of the last relevant
|
||||
// event in the room.
|
||||
// "notifications": boolean, Whether or not the user has been
|
||||
// highlighted on any unread events.
|
||||
// "unread": boolean, Whether or not the user has any
|
||||
// unread events.
|
||||
//
|
||||
// All of the cached values are lazily loaded on read in the
|
||||
// recents comparator. When an event is received for a particular
|
||||
// room, all the cached values are invalidated - forcing the
|
||||
// next read to set new values. The entries do not expire on
|
||||
// their own.
|
||||
roomCache: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -84,6 +103,8 @@ class RoomListStore extends Store {
|
|||
!payload.isLiveUnfilteredRoomTimelineEvent ||
|
||||
!this._eventTriggersRecentReorder(payload.event)
|
||||
) break;
|
||||
|
||||
this._clearCachedRoomState(payload.event.getRoomId());
|
||||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
|
@ -111,6 +132,8 @@ class RoomListStore extends Store {
|
|||
if (liveTimeline !== eventTimeline ||
|
||||
!this._eventTriggersRecentReorder(payload.event)
|
||||
) break;
|
||||
|
||||
this._clearCachedRoomState(payload.event.getRoomId());
|
||||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
|
@ -119,6 +142,13 @@ class RoomListStore extends Store {
|
|||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
case 'MatrixActions.Room.accountData': {
|
||||
if (payload.event_type === 'm.fully_read') {
|
||||
this._clearCachedRoomState(payload.room.roomId);
|
||||
this._generateRoomLists();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'MatrixActions.Room.myMembership': {
|
||||
this._generateRoomLists();
|
||||
}
|
||||
|
@ -216,11 +246,18 @@ class RoomListStore extends Store {
|
|||
}
|
||||
});
|
||||
|
||||
// Note: we check the settings up here instead of in the forEach or
|
||||
// in the _recentsComparator to avoid hitting the SettingsStore a few
|
||||
// thousand times.
|
||||
const pinUnread = SettingsStore.getValue("pinUnreadRooms");
|
||||
const pinMentioned = SettingsStore.getValue("pinMentionedRooms");
|
||||
Object.keys(lists).forEach((listKey) => {
|
||||
let comparator;
|
||||
switch (RoomListStore._listOrders[listKey]) {
|
||||
case "recent":
|
||||
comparator = this._recentsComparator;
|
||||
comparator = (roomA, roomB) => {
|
||||
return this._recentsComparator(roomA, roomB, pinUnread, pinMentioned);
|
||||
};
|
||||
break;
|
||||
case "manual":
|
||||
default:
|
||||
|
@ -236,6 +273,44 @@ class RoomListStore extends Store {
|
|||
});
|
||||
}
|
||||
|
||||
_updateCachedRoomState(roomId, type, value) {
|
||||
const roomCache = this._state.roomCache;
|
||||
if (!roomCache[roomId]) roomCache[roomId] = {};
|
||||
|
||||
if (value) roomCache[roomId][type] = value;
|
||||
else delete roomCache[roomId][type];
|
||||
|
||||
this._setState({roomCache});
|
||||
}
|
||||
|
||||
_clearCachedRoomState(roomId) {
|
||||
const roomCache = this._state.roomCache;
|
||||
delete roomCache[roomId];
|
||||
this._setState({roomCache});
|
||||
}
|
||||
|
||||
_getRoomState(room, type) {
|
||||
const roomId = room.roomId;
|
||||
const roomCache = this._state.roomCache;
|
||||
if (roomCache[roomId] && typeof roomCache[roomId][type] !== 'undefined') {
|
||||
return roomCache[roomId][type];
|
||||
}
|
||||
|
||||
if (type === "timestamp") {
|
||||
const ts = this._tsOfNewestEvent(room);
|
||||
this._updateCachedRoomState(roomId, "timestamp", ts);
|
||||
return ts;
|
||||
} else if (type === "unread") {
|
||||
const unread = room.getUnreadNotificationCount() > 0;
|
||||
this._updateCachedRoomState(roomId, "unread", unread);
|
||||
return unread;
|
||||
} else if (type === "notifications") {
|
||||
const notifs = room.getUnreadNotificationCount("highlight") > 0;
|
||||
this._updateCachedRoomState(roomId, "notifications", notifs);
|
||||
return notifs;
|
||||
} else throw new Error("Unrecognized room cache type: " + type);
|
||||
}
|
||||
|
||||
_eventTriggersRecentReorder(ev) {
|
||||
return ev.getTs() && (
|
||||
Unread.eventTriggersUnreadCount(ev) ||
|
||||
|
@ -261,10 +336,40 @@ class RoomListStore extends Store {
|
|||
}
|
||||
}
|
||||
|
||||
_recentsComparator(roomA, roomB) {
|
||||
// XXX: We could use a cache here and update it when we see new
|
||||
// events that trigger a reorder
|
||||
return this._tsOfNewestEvent(roomB) - this._tsOfNewestEvent(roomA);
|
||||
_recentsComparator(roomA, roomB, pinUnread, pinMentioned) {
|
||||
// We try and set the ordering to be Mentioned > Unread > Recent
|
||||
// assuming the user has the right settings, of course.
|
||||
|
||||
const timestampA = this._getRoomState(roomA, "timestamp");
|
||||
const timestampB = this._getRoomState(roomB, "timestamp");
|
||||
const timestampDiff = timestampB - timestampA;
|
||||
|
||||
if (pinMentioned) {
|
||||
const mentionsA = this._getRoomState(roomA, "notifications");
|
||||
const mentionsB = this._getRoomState(roomB, "notifications");
|
||||
if (mentionsA && !mentionsB) return -1;
|
||||
if (!mentionsA && mentionsB) return 1;
|
||||
|
||||
// If they both have notifications, sort by timestamp.
|
||||
// If neither have notifications (the fourth check not shown
|
||||
// here), then try and sort by unread messages and finally by
|
||||
// timestamp.
|
||||
if (mentionsA && mentionsB) return timestampDiff;
|
||||
}
|
||||
|
||||
if (pinUnread) {
|
||||
const unreadA = this._getRoomState(roomA, "unread");
|
||||
const unreadB = this._getRoomState(roomB, "unread");
|
||||
if (unreadA && !unreadB) return -1;
|
||||
if (!unreadA && unreadB) return 1;
|
||||
|
||||
// If they both have unread messages, sort by timestamp
|
||||
// If nether have unread message (the fourth check not shown
|
||||
// here), then just sort by timestamp anyways.
|
||||
if (unreadA && unreadB) return timestampDiff;
|
||||
}
|
||||
|
||||
return timestampDiff;
|
||||
}
|
||||
|
||||
_lexicographicalComparator(roomA, roomB) {
|
||||
|
|
Loading…
Reference in a new issue