Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit e9f59ed

Browse files
authored
Merge pull request #5943 from matrix-org/t3chguy/fix/17082
Sort rooms in the add existing to space dialog based on recency
2 parents 8dbcc85 + bed5231 commit e9f59ed

File tree

2 files changed

+72
-65
lines changed

2 files changed

+72
-65
lines changed

src/components/views/dialogs/AddExistingToSpaceDialog.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React, {useContext, useState} from "react";
17+
import React, {useContext, useMemo, useState} from "react";
1818
import classNames from "classnames";
1919
import {Room} from "matrix-js-sdk/src/models/room";
2020
import {MatrixClient} from "matrix-js-sdk/src/client";
@@ -34,6 +34,7 @@ import DMRoomMap from "../../../utils/DMRoomMap";
3434
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
3535
import StyledCheckbox from "../elements/StyledCheckbox";
3636
import MatrixClientContext from "../../../contexts/MatrixClientContext";
37+
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
3738

3839
interface IProps extends IDialogProps {
3940
matrixClient: MatrixClient;
@@ -57,6 +58,8 @@ interface IAddExistingToSpaceProps {
5758

5859
export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space, selected, onChange }) => {
5960
const cli = useContext(MatrixClientContext);
61+
const visibleRooms = useMemo(() => sortRooms(cli.getVisibleRooms()), [cli]);
62+
6063
const [query, setQuery] = useState("");
6164
const lcQuery = query.toLowerCase();
6265

@@ -65,7 +68,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
6568
const existingRoomsSet = new Set(SpaceStore.instance.getChildRooms(space.roomId));
6669

6770
const joinRule = space.getJoinRule();
68-
const [spaces, rooms, dms] = cli.getVisibleRooms().reduce((arr, room) => {
71+
const [spaces, rooms, dms] = visibleRooms.reduce((arr, room) => {
6972
if (room.getMyMembership() !== "join") return arr;
7073
if (!room.name.toLowerCase().includes(lcQuery)) return arr;
7174

src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts

Lines changed: 67 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -21,79 +21,83 @@ import { MatrixClientPeg } from "../../../../MatrixClientPeg";
2121
import * as Unread from "../../../../Unread";
2222
import { EffectiveMembership, getEffectiveMembership } from "../../../../utils/membership";
2323

24-
/**
25-
* Sorts rooms according to the last event's timestamp in each room that seems
26-
* useful to the user.
27-
*/
28-
export class RecentAlgorithm implements IAlgorithm {
29-
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
30-
// We cache the timestamp lookup to avoid iterating forever on the timeline
31-
// of events. This cache only survives a single sort though.
32-
// We wouldn't need this if `.sort()` didn't constantly try and compare all
33-
// of the rooms to each other.
34-
35-
// TODO: We could probably improve the sorting algorithm here by finding changes.
36-
// See https://github.com/vector-im/element-web/issues/14459
37-
// For example, if we spent a little bit of time to determine which elements have
38-
// actually changed (probably needs to be done higher up?) then we could do an
39-
// insertion sort or similar on the limited set of changes.
40-
41-
// TODO: Don't assume we're using the same client as the peg
42-
// See https://github.com/vector-im/element-web/issues/14458
43-
let myUserId = '';
44-
if (MatrixClientPeg.get()) {
45-
myUserId = MatrixClientPeg.get().getUserId();
24+
export const sortRooms = (rooms: Room[]): Room[] => {
25+
// We cache the timestamp lookup to avoid iterating forever on the timeline
26+
// of events. This cache only survives a single sort though.
27+
// We wouldn't need this if `.sort()` didn't constantly try and compare all
28+
// of the rooms to each other.
29+
30+
// TODO: We could probably improve the sorting algorithm here by finding changes.
31+
// See https://github.com/vector-im/element-web/issues/14459
32+
// For example, if we spent a little bit of time to determine which elements have
33+
// actually changed (probably needs to be done higher up?) then we could do an
34+
// insertion sort or similar on the limited set of changes.
35+
36+
// TODO: Don't assume we're using the same client as the peg
37+
// See https://github.com/vector-im/element-web/issues/14458
38+
let myUserId = '';
39+
if (MatrixClientPeg.get()) {
40+
myUserId = MatrixClientPeg.get().getUserId();
41+
}
42+
43+
const tsCache: { [roomId: string]: number } = {};
44+
const getLastTs = (r: Room) => {
45+
if (tsCache[r.roomId]) {
46+
return tsCache[r.roomId];
4647
}
4748

48-
const tsCache: { [roomId: string]: number } = {};
49-
const getLastTs = (r: Room) => {
50-
if (tsCache[r.roomId]) {
51-
return tsCache[r.roomId];
49+
const ts = (() => {
50+
// Apparently we can have rooms without timelines, at least under testing
51+
// environments. Just return MAX_INT when this happens.
52+
if (!r || !r.timeline) {
53+
return Number.MAX_SAFE_INTEGER;
5254
}
5355

54-
const ts = (() => {
55-
// Apparently we can have rooms without timelines, at least under testing
56-
// environments. Just return MAX_INT when this happens.
57-
if (!r || !r.timeline) {
58-
return Number.MAX_SAFE_INTEGER;
59-
}
60-
61-
// If the room hasn't been joined yet, it probably won't have a timeline to
62-
// parse. We'll still fall back to the timeline if this fails, but chances
63-
// are we'll at least have our own membership event to go off of.
64-
const effectiveMembership = getEffectiveMembership(r.getMyMembership());
65-
if (effectiveMembership !== EffectiveMembership.Join) {
66-
const membershipEvent = r.currentState.getStateEvents("m.room.member", myUserId);
67-
if (membershipEvent && !Array.isArray(membershipEvent)) {
68-
return membershipEvent.getTs();
69-
}
56+
// If the room hasn't been joined yet, it probably won't have a timeline to
57+
// parse. We'll still fall back to the timeline if this fails, but chances
58+
// are we'll at least have our own membership event to go off of.
59+
const effectiveMembership = getEffectiveMembership(r.getMyMembership());
60+
if (effectiveMembership !== EffectiveMembership.Join) {
61+
const membershipEvent = r.currentState.getStateEvents("m.room.member", myUserId);
62+
if (membershipEvent && !Array.isArray(membershipEvent)) {
63+
return membershipEvent.getTs();
7064
}
65+
}
7166

72-
for (let i = r.timeline.length - 1; i >= 0; --i) {
73-
const ev = r.timeline[i];
74-
if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?)
67+
for (let i = r.timeline.length - 1; i >= 0; --i) {
68+
const ev = r.timeline[i];
69+
if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?)
7570

76-
if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) {
77-
return ev.getTs();
78-
}
71+
if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) {
72+
return ev.getTs();
7973
}
74+
}
8075

81-
// we might only have events that don't trigger the unread indicator,
82-
// in which case use the oldest event even if normally it wouldn't count.
83-
// This is better than just assuming the last event was forever ago.
84-
if (r.timeline.length && r.timeline[0].getTs()) {
85-
return r.timeline[0].getTs();
86-
} else {
87-
return Number.MAX_SAFE_INTEGER;
88-
}
89-
})();
76+
// we might only have events that don't trigger the unread indicator,
77+
// in which case use the oldest event even if normally it wouldn't count.
78+
// This is better than just assuming the last event was forever ago.
79+
if (r.timeline.length && r.timeline[0].getTs()) {
80+
return r.timeline[0].getTs();
81+
} else {
82+
return Number.MAX_SAFE_INTEGER;
83+
}
84+
})();
85+
86+
tsCache[r.roomId] = ts;
87+
return ts;
88+
};
9089

91-
tsCache[r.roomId] = ts;
92-
return ts;
93-
};
90+
return rooms.sort((a, b) => {
91+
return getLastTs(b) - getLastTs(a);
92+
});
93+
};
9494

95-
return rooms.sort((a, b) => {
96-
return getLastTs(b) - getLastTs(a);
97-
});
95+
/**
96+
* Sorts rooms according to the last event's timestamp in each room that seems
97+
* useful to the user.
98+
*/
99+
export class RecentAlgorithm implements IAlgorithm {
100+
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
101+
return sortRooms(rooms);
98102
}
99103
}

0 commit comments

Comments
 (0)