mirror of
https://github.com/element-hq/element-web.git
synced 2024-11-30 23:31:28 +03:00
Make RoomListStore aware of sub list orderings
so that it can do optimistic updates of ordered lists.
This commit is contained in:
parent
21d70125e4
commit
9b0df19149
2 changed files with 103 additions and 75 deletions
|
@ -116,8 +116,9 @@ RoomListActions.tagRoom = function(matrixClient, room, oldTag, newTag, oldIndex,
|
||||||
if (newTag && newTag !== 'im.vector.fake.direct' &&
|
if (newTag && newTag !== 'im.vector.fake.direct' &&
|
||||||
(hasChangedSubLists || metaData)
|
(hasChangedSubLists || metaData)
|
||||||
) {
|
) {
|
||||||
// Optimistic update of what will happen to the room tags
|
// metaData is the body of the PUT to set the tag, so it must
|
||||||
room.tags[newTag] = metaData || {};
|
// at least be an empty object.
|
||||||
|
metaData = metaData || {};
|
||||||
|
|
||||||
const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function(err) {
|
const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function(err) {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
import {Store} from 'flux/utils';
|
import {Store} from 'flux/utils';
|
||||||
import dis from '../dispatcher';
|
import dis from '../dispatcher';
|
||||||
import DMRoomMap from '../utils/DMRoomMap';
|
import DMRoomMap from '../utils/DMRoomMap';
|
||||||
|
import Unread from '../Unread';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for storing application state for categorising rooms in
|
* A class for storing application state for categorising rooms in
|
||||||
|
@ -26,6 +27,8 @@ class RoomListStore extends Store {
|
||||||
super(dis);
|
super(dis);
|
||||||
|
|
||||||
this._init();
|
this._init();
|
||||||
|
this._getManualComparator = this._getManualComparator.bind(this);
|
||||||
|
this._recentsComparator = this._recentsComparator.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_init() {
|
_init() {
|
||||||
|
@ -62,7 +65,7 @@ class RoomListStore extends Store {
|
||||||
break;
|
break;
|
||||||
case 'MatrixActions.Room.tags': {
|
case 'MatrixActions.Room.tags': {
|
||||||
if (!this._state.ready) break;
|
if (!this._state.ready) break;
|
||||||
this._updateRoomLists(payload.room);
|
this._generateRoomLists();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'MatrixActions.accountData': {
|
case 'MatrixActions.accountData': {
|
||||||
|
@ -76,12 +79,7 @@ class RoomListStore extends Store {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'RoomListActions.tagRoom.pending': {
|
case 'RoomListActions.tagRoom.pending': {
|
||||||
this._updateRoomListsOptimistic(
|
this._generateRoomLists(payload.request);
|
||||||
payload.request.room,
|
|
||||||
payload.request.oldTag,
|
|
||||||
payload.request.newTag,
|
|
||||||
payload.request.metaData,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'RoomListActions.tagRoom.failure': {
|
case 'RoomListActions.tagRoom.failure': {
|
||||||
|
@ -93,76 +91,13 @@ class RoomListStore extends Store {
|
||||||
// Reset state without pushing an update to the view, which generally assumes that
|
// Reset state without pushing an update to the view, which generally assumes that
|
||||||
// the matrix client isn't `null` and so causing a re-render will cause NPEs.
|
// the matrix client isn't `null` and so causing a re-render will cause NPEs.
|
||||||
this._init();
|
this._init();
|
||||||
|
this._matrixClient = null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateRoomListsOptimistic(updatedRoom, oldTag, newTag, metaData) {
|
_generateRoomLists(optimisticRequest) {
|
||||||
const newLists = {};
|
|
||||||
|
|
||||||
// Adding a tag to an untagged room - need to remove it from recents
|
|
||||||
if (newTag && Object.keys(updatedRoom.tags).length === 0) {
|
|
||||||
oldTag = 'im.vector.fake.recent';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing a tag from a room with one tag left - need to add it to recents
|
|
||||||
if (oldTag && Object.keys(updatedRoom.tags).length === 1) {
|
|
||||||
newTag = 'im.vector.fake.recent';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove room from oldTag
|
|
||||||
Object.keys(this._state.lists).forEach((tagName) => {
|
|
||||||
if (tagName === oldTag) {
|
|
||||||
newLists[tagName] = this._state.lists[tagName].filter((room) => {
|
|
||||||
return room.roomId !== updatedRoom.roomId;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
newLists[tagName] = this._state.lists[tagName];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/// XXX: RoomSubList sorts by data on the room object. We
|
|
||||||
/// should sort in advance and incrementally insert new rooms
|
|
||||||
/// instead of resorting every time.
|
|
||||||
if (metaData) {
|
|
||||||
updatedRoom.tags[newTag] = metaData;
|
|
||||||
}
|
|
||||||
|
|
||||||
newLists[newTag].push(updatedRoom);
|
|
||||||
|
|
||||||
this._setState({
|
|
||||||
lists: newLists,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateRoomLists(updatedRoom) {
|
|
||||||
const roomTags = Object.keys(updatedRoom.tags);
|
|
||||||
|
|
||||||
const newLists = {};
|
|
||||||
|
|
||||||
// Removal of the updatedRoom from tags it no longer has
|
|
||||||
Object.keys(this._state.lists).forEach((tagName) => {
|
|
||||||
newLists[tagName] = this._state.lists[tagName].filter((room) => {
|
|
||||||
return room.roomId !== updatedRoom.roomId || roomTags.includes(tagName);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
roomTags.forEach((tagName) => {
|
|
||||||
if (newLists[tagName].includes(updatedRoom)) return;
|
|
||||||
newLists[tagName].push(updatedRoom);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (roomTags.length === 0) {
|
|
||||||
newLists['im.vector.fake.recent'].unshift(updatedRoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setState({
|
|
||||||
lists: newLists,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_generateRoomLists() {
|
|
||||||
const lists = {
|
const lists = {
|
||||||
"im.vector.fake.invite": [],
|
"im.vector.fake.invite": [],
|
||||||
"m.favourite": [],
|
"m.favourite": [],
|
||||||
|
@ -187,7 +122,19 @@ class RoomListStore extends Store {
|
||||||
} else if (me.membership == "join" || me.membership === "ban" ||
|
} else if (me.membership == "join" || me.membership === "ban" ||
|
||||||
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) {
|
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) {
|
||||||
// Used to split rooms via tags
|
// Used to split rooms via tags
|
||||||
const tagNames = Object.keys(room.tags);
|
let tagNames = Object.keys(room.tags);
|
||||||
|
|
||||||
|
if (optimisticRequest && optimisticRequest.room === room) {
|
||||||
|
// Remove old tag
|
||||||
|
tagNames = tagNames.filter((tagName) => tagName !== optimisticRequest.oldTag);
|
||||||
|
// Add new tag
|
||||||
|
if (optimisticRequest.newTag &&
|
||||||
|
!tagNames.includes(optimisticRequest.newTag)
|
||||||
|
) {
|
||||||
|
tagNames.push(optimisticRequest.newTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tagNames.length) {
|
if (tagNames.length) {
|
||||||
for (let i = 0; i < tagNames.length; i++) {
|
for (let i = 0; i < tagNames.length; i++) {
|
||||||
const tagName = tagNames[i];
|
const tagName = tagNames[i];
|
||||||
|
@ -207,12 +154,92 @@ class RoomListStore extends Store {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const listOrders = {
|
||||||
|
"manual": [
|
||||||
|
"m.favourite",
|
||||||
|
],
|
||||||
|
"recent": [
|
||||||
|
"im.vector.fake.invite",
|
||||||
|
"im.vector.fake.recent",
|
||||||
|
"im.vector.fake.direct",
|
||||||
|
"m.lowpriority",
|
||||||
|
"im.vector.fake.archived",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(listOrders).forEach((order) => {
|
||||||
|
listOrders[order].forEach((listKey) => {
|
||||||
|
let comparator;
|
||||||
|
switch (order) {
|
||||||
|
case "manual":
|
||||||
|
comparator = this._getManualComparator(listKey, optimisticRequest);
|
||||||
|
break;
|
||||||
|
case "recent":
|
||||||
|
comparator = this._recentsComparator;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lists[listKey].sort(comparator);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this._setState({
|
this._setState({
|
||||||
lists,
|
lists,
|
||||||
ready: true, // Ready to receive updates via Room.tags events
|
ready: true, // Ready to receive updates via Room.tags events
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_tsOfNewestEvent(room) {
|
||||||
|
for (let i = room.timeline.length - 1; i >= 0; --i) {
|
||||||
|
const ev = room.timeline[i];
|
||||||
|
if (ev.getTs() &&
|
||||||
|
(Unread.eventTriggersUnreadCount(ev) ||
|
||||||
|
(ev.getSender() === this._matrixClient.credentials.userId))
|
||||||
|
) {
|
||||||
|
return ev.getTs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we might only have events that don't trigger the unread indicator,
|
||||||
|
// in which case use the oldest event even if normally it wouldn't count.
|
||||||
|
// This is better than just assuming the last event was forever ago.
|
||||||
|
if (room.timeline.length && room.timeline[0].getTs()) {
|
||||||
|
return room.timeline[0].getTs();
|
||||||
|
} else {
|
||||||
|
return Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_recentsComparator(roomA, roomB) {
|
||||||
|
return this._tsOfNewestEvent(roomB) - this._tsOfNewestEvent(roomA);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lexicographicalComparator(roomA, roomB) {
|
||||||
|
return roomA.name > roomB.name ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getManualComparator(tagName, optimisticRequest) {
|
||||||
|
return (roomA, roomB) => {
|
||||||
|
let metaA = roomA.tags[tagName];
|
||||||
|
let metaB = roomB.tags[tagName];
|
||||||
|
|
||||||
|
if (optimisticRequest && roomA === optimisticRequest.room) metaA = optimisticRequest.metaData;
|
||||||
|
if (optimisticRequest && roomB === optimisticRequest.room) metaB = optimisticRequest.metaData;
|
||||||
|
|
||||||
|
// Make sure the room tag has an order element, if not set it to be the bottom
|
||||||
|
const a = metaA.order;
|
||||||
|
const b = metaB.order;
|
||||||
|
|
||||||
|
// Order undefined room tag orders to the bottom
|
||||||
|
if (a === undefined && b !== undefined) {
|
||||||
|
return 1;
|
||||||
|
} else if (a !== undefined && b === undefined) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a == b ? this._lexicographicalComparator(roomA, roomB) : ( a > b ? 1 : -1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
getRoomLists() {
|
getRoomLists() {
|
||||||
return this._state.lists;
|
return this._state.lists;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue