mirror of
https://github.com/element-hq/element-web
synced 2024-11-28 20:38:55 +03:00
Merge pull request #4085 from matrix-org/t3chguy/alpha_room_list
Room List sorting algorithms
This commit is contained in:
commit
48dc671bcb
7 changed files with 150 additions and 100 deletions
|
@ -14,6 +14,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_PreferencesUserSettingsTab .mx_Field {
|
||||
.mx_PreferencesUserSettingsTab {
|
||||
.mx_Field {
|
||||
@mixin mx_Settings_fullWidthField;
|
||||
}
|
||||
|
||||
.mx_SettingsTab_section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,6 @@ export default class RoomSubList extends React.PureComponent {
|
|||
tagName: PropTypes.string,
|
||||
addRoomLabel: PropTypes.string,
|
||||
|
||||
order: PropTypes.string.isRequired,
|
||||
|
||||
// passed through to RoomTile and used to highlight room with `!` regardless of notifications count
|
||||
isInvite: PropTypes.bool,
|
||||
|
||||
|
|
|
@ -700,13 +700,11 @@ export default createReactClass({
|
|||
list: [],
|
||||
extraTiles: this._makeGroupInviteTiles(this.props.searchFilter),
|
||||
label: _t('Community Invites'),
|
||||
order: "recent",
|
||||
isInvite: true,
|
||||
},
|
||||
{
|
||||
list: this.state.lists['im.vector.fake.invite'],
|
||||
label: _t('Invites'),
|
||||
order: "recent",
|
||||
incomingCall: incomingCallIfTaggedAs('im.vector.fake.invite'),
|
||||
isInvite: true,
|
||||
},
|
||||
|
@ -714,14 +712,12 @@ export default createReactClass({
|
|||
list: this.state.lists['m.favourite'],
|
||||
label: _t('Favourites'),
|
||||
tagName: "m.favourite",
|
||||
order: "manual",
|
||||
incomingCall: incomingCallIfTaggedAs('m.favourite'),
|
||||
},
|
||||
{
|
||||
list: this.state.lists[TAG_DM],
|
||||
label: _t('Direct Messages'),
|
||||
tagName: TAG_DM,
|
||||
order: "recent",
|
||||
incomingCall: incomingCallIfTaggedAs(TAG_DM),
|
||||
onAddRoom: () => {dis.dispatch({action: 'view_create_chat'});},
|
||||
addRoomLabel: _t("Start chat"),
|
||||
|
@ -729,7 +725,6 @@ export default createReactClass({
|
|||
{
|
||||
list: this.state.lists['im.vector.fake.recent'],
|
||||
label: _t('Rooms'),
|
||||
order: "recent",
|
||||
incomingCall: incomingCallIfTaggedAs('im.vector.fake.recent'),
|
||||
onAddRoom: () => {dis.dispatch({action: 'view_create_room'});},
|
||||
},
|
||||
|
@ -744,7 +739,6 @@ export default createReactClass({
|
|||
key: tagName,
|
||||
label: labelForTagName(tagName),
|
||||
tagName: tagName,
|
||||
order: "manual",
|
||||
incomingCall: incomingCallIfTaggedAs(tagName),
|
||||
};
|
||||
});
|
||||
|
@ -754,13 +748,11 @@ export default createReactClass({
|
|||
list: this.state.lists['m.lowpriority'],
|
||||
label: _t('Low priority'),
|
||||
tagName: "m.lowpriority",
|
||||
order: "recent",
|
||||
incomingCall: incomingCallIfTaggedAs('m.lowpriority'),
|
||||
},
|
||||
{
|
||||
list: this.state.lists['im.vector.fake.archived'],
|
||||
label: _t('Historical'),
|
||||
order: "recent",
|
||||
incomingCall: incomingCallIfTaggedAs('im.vector.fake.archived'),
|
||||
startAsHidden: true,
|
||||
showSpinner: this.state.isLoadingLeftRooms,
|
||||
|
@ -770,7 +762,6 @@ export default createReactClass({
|
|||
list: this.state.lists['m.server_notice'],
|
||||
label: _t('System Alerts'),
|
||||
tagName: "m.lowpriority",
|
||||
order: "recent",
|
||||
incomingCall: incomingCallIfTaggedAs('m.server_notice'),
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -25,6 +25,12 @@ import * as sdk from "../../../../..";
|
|||
import PlatformPeg from "../../../../../PlatformPeg";
|
||||
|
||||
export default class PreferencesUserSettingsTab extends React.Component {
|
||||
static ROOM_LIST_SETTINGS = [
|
||||
'RoomList.orderAlphabetically',
|
||||
'RoomList.orderByImportance',
|
||||
'breadcrumbs',
|
||||
];
|
||||
|
||||
static COMPOSER_SETTINGS = [
|
||||
'MessageComposerInput.autoReplaceEmoji',
|
||||
'MessageComposerInput.suggestEmoji',
|
||||
|
@ -47,11 +53,6 @@ export default class PreferencesUserSettingsTab extends React.Component {
|
|||
'showImages',
|
||||
];
|
||||
|
||||
static ROOM_LIST_SETTINGS = [
|
||||
'RoomList.orderByImportance',
|
||||
'breadcrumbs',
|
||||
];
|
||||
|
||||
static ADVANCED_SETTINGS = [
|
||||
'alwaysShowEncryptionIcons',
|
||||
'Pill.shouldShowPillAvatar',
|
||||
|
@ -173,15 +174,21 @@ export default class PreferencesUserSettingsTab extends React.Component {
|
|||
<div className="mx_SettingsTab_heading">{_t("Preferences")}</div>
|
||||
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
|
||||
{this._renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
|
||||
|
||||
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
|
||||
{this._renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
|
||||
|
||||
<span className="mx_SettingsTab_subheading">{_t("Room list")}</span>
|
||||
{this._renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
|
||||
</div>
|
||||
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
|
||||
{this._renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
|
||||
</div>
|
||||
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
|
||||
{this._renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
|
||||
</div>
|
||||
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Advanced")}</span>
|
||||
{this._renderGroup(PreferencesUserSettingsTab.ADVANCED_SETTINGS)}
|
||||
{minimizeToTrayOption}
|
||||
|
|
|
@ -414,8 +414,9 @@
|
|||
"Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets",
|
||||
"Prompt before sending invites to potentially invalid matrix IDs": "Prompt before sending invites to potentially invalid matrix IDs",
|
||||
"Show developer tools": "Show developer tools",
|
||||
"Order rooms in the room list by most important first instead of most recent": "Order rooms in the room list by most important first instead of most recent",
|
||||
"Show recently visited rooms above the room list": "Show recently visited rooms above the room list",
|
||||
"Order rooms by name": "Order rooms by name",
|
||||
"Show rooms with unread notifications first": "Show rooms with unread notifications first",
|
||||
"Show shortcuts to recently viewed rooms above the room list": "Show shortcuts to recently viewed rooms above the room list",
|
||||
"Show hidden events in timeline": "Show hidden events in timeline",
|
||||
"Low bandwidth mode": "Low bandwidth mode",
|
||||
"Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)",
|
||||
|
@ -774,9 +775,9 @@
|
|||
"Always show the window menu bar": "Always show the window menu bar",
|
||||
"Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close",
|
||||
"Preferences": "Preferences",
|
||||
"Room list": "Room list",
|
||||
"Composer": "Composer",
|
||||
"Timeline": "Timeline",
|
||||
"Room list": "Room list",
|
||||
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
||||
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)",
|
||||
"Read Marker off-screen lifetime (ms)": "Read Marker off-screen lifetime (ms)",
|
||||
|
|
|
@ -433,14 +433,19 @@ export const SETTINGS = {
|
|||
deny: [],
|
||||
},
|
||||
},
|
||||
"RoomList.orderAlphabetically": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("Order rooms by name"),
|
||||
default: false,
|
||||
},
|
||||
"RoomList.orderByImportance": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td('Order rooms in the room list by most important first instead of most recent'),
|
||||
displayName: _td("Show rooms with unread notifications first"),
|
||||
default: true,
|
||||
},
|
||||
"breadcrumbs": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("Show recently visited rooms above the room list"),
|
||||
displayName: _td("Show shortcuts to recently viewed rooms above the room list"),
|
||||
default: true,
|
||||
},
|
||||
"showHiddenEventsInTimeline": {
|
||||
|
|
|
@ -36,32 +36,52 @@ const CATEGORY_GREY = "grey"; // Unread notified messages (not mentions)
|
|||
const CATEGORY_BOLD = "bold"; // Unread messages (not notified, 'Mentions Only' rooms)
|
||||
const CATEGORY_IDLE = "idle"; // Nothing of interest
|
||||
|
||||
const CATEGORY_ORDER = [CATEGORY_RED, CATEGORY_GREY, CATEGORY_BOLD, CATEGORY_IDLE];
|
||||
|
||||
export const TAG_DM = "im.vector.fake.direct";
|
||||
|
||||
const LIST_ORDERS = {
|
||||
"m.favourite": "manual",
|
||||
"im.vector.fake.invite": "recent",
|
||||
"im.vector.fake.recent": "recent",
|
||||
[TAG_DM]: "recent",
|
||||
"m.lowpriority": "recent",
|
||||
"im.vector.fake.archived": "recent",
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifier for the "breadcrumb" (or "sort by most important room first") algorithm.
|
||||
* Includes a provision for keeping the currently open room from flying down the room
|
||||
* list.
|
||||
* Identifier for manual sorting behaviour: sort by the user defined order.
|
||||
* @type {string}
|
||||
*/
|
||||
const ALGO_IMPORTANCE = "importance";
|
||||
export const ALGO_MANUAL = "manual";
|
||||
|
||||
/**
|
||||
* Identifier for alphabetic sorting behaviour: sort by the room name alphabetically first.
|
||||
* @type {string}
|
||||
*/
|
||||
export const ALGO_ALPHABETIC = "alphabetic";
|
||||
|
||||
/**
|
||||
* Identifier for classic sorting behaviour: sort by the most recent message first.
|
||||
* @type {string}
|
||||
*/
|
||||
const ALGO_RECENT = "recent";
|
||||
export const ALGO_RECENT = "recent";
|
||||
|
||||
const CATEGORY_ORDER = [CATEGORY_RED, CATEGORY_GREY, CATEGORY_BOLD, CATEGORY_IDLE];
|
||||
|
||||
const getListAlgorithm = (listKey, settingAlgorithm) => {
|
||||
// apply manual sorting only to m.favourite, otherwise respect the global setting
|
||||
// all the known tags are listed explicitly here to simplify future changes
|
||||
switch (listKey) {
|
||||
case "m.favourite":
|
||||
return ALGO_MANUAL;
|
||||
case "im.vector.fake.invite":
|
||||
case "im.vector.fake.recent":
|
||||
case "im.vector.fake.archived":
|
||||
case "m.lowpriority":
|
||||
case TAG_DM:
|
||||
default:
|
||||
return settingAlgorithm;
|
||||
}
|
||||
};
|
||||
|
||||
const knownLists = new Set([
|
||||
"m.favourite",
|
||||
"im.vector.fake.invite",
|
||||
"im.vector.fake.recent",
|
||||
"im.vector.fake.archived",
|
||||
"m.lowpriority",
|
||||
TAG_DM,
|
||||
]);
|
||||
|
||||
/**
|
||||
* A class for storing application state for categorising rooms in
|
||||
|
@ -79,13 +99,13 @@ class RoomListStore extends Store {
|
|||
/**
|
||||
* Changes the sorting algorithm used by the RoomListStore.
|
||||
* @param {string} algorithm The new algorithm to use. Should be one of the ALGO_* constants.
|
||||
* @param {boolean} orderImportantFirst Whether to sort by categories of importance
|
||||
*/
|
||||
updateSortingAlgorithm(algorithm) {
|
||||
updateSortingAlgorithm(algorithm, orderImportantFirst) {
|
||||
// Dev note: We only have two algorithms at the moment, but it isn't impossible that we want
|
||||
// multiple in the future. Also constants make things slightly clearer.
|
||||
const byImportance = algorithm === ALGO_IMPORTANCE;
|
||||
console.log("Updating room sorting algorithm: sortByImportance=" + byImportance);
|
||||
this._setState({orderRoomsByImportance: byImportance});
|
||||
console.log("Updating room sorting algorithm: ", {algorithm, orderImportantFirst});
|
||||
this._setState({algorithm, orderImportantFirst});
|
||||
|
||||
// Trigger a resort of the entire list to reflect the change in algorithm
|
||||
this._generateInitialRoomLists();
|
||||
|
@ -109,9 +129,11 @@ class RoomListStore extends Store {
|
|||
presentationLists: defaultLists, // like `lists`, but with arrays of rooms instead
|
||||
ready: false,
|
||||
stickyRoomId: null,
|
||||
orderRoomsByImportance: true,
|
||||
algorithm: ALGO_RECENT,
|
||||
orderImportantFirst: false,
|
||||
};
|
||||
|
||||
SettingsStore.monitorSetting('RoomList.orderAlphabetically', null);
|
||||
SettingsStore.monitorSetting('RoomList.orderByImportance', null);
|
||||
SettingsStore.monitorSetting('feature_custom_tags', null);
|
||||
}
|
||||
|
@ -138,11 +160,18 @@ class RoomListStore extends Store {
|
|||
case 'setting_updated': {
|
||||
if (!logicallyReady) break;
|
||||
|
||||
if (payload.settingName === 'RoomList.orderByImportance') {
|
||||
this.updateSortingAlgorithm(payload.newValue === true ? ALGO_IMPORTANCE : ALGO_RECENT);
|
||||
} else if (payload.settingName === 'feature_custom_tags') {
|
||||
switch (payload.settingName) {
|
||||
case "RoomList.orderAlphabetically":
|
||||
this.updateSortingAlgorithm(payload.newValue ? ALGO_ALPHABETIC : ALGO_RECENT,
|
||||
this._state.orderImportantFirst);
|
||||
break;
|
||||
case "RoomList.orderByImportance":
|
||||
this.updateSortingAlgorithm(this._state.algorithm, payload.newValue);
|
||||
break;
|
||||
case "feature_custom_tags":
|
||||
this._setState({tagsEnabled: payload.newValue});
|
||||
this._generateInitialRoomLists(); // Tags means we have to start from scratch
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -160,9 +189,9 @@ class RoomListStore extends Store {
|
|||
|
||||
this._matrixClient = payload.matrixClient;
|
||||
|
||||
const algorithm = SettingsStore.getValue("RoomList.orderByImportance")
|
||||
? ALGO_IMPORTANCE : ALGO_RECENT;
|
||||
this.updateSortingAlgorithm(algorithm);
|
||||
const orderByImportance = SettingsStore.getValue("RoomList.orderByImportance");
|
||||
const orderAlphabetically = SettingsStore.getValue("RoomList.orderAlphabetically");
|
||||
this.updateSortingAlgorithm(orderAlphabetically ? ALGO_ALPHABETIC : ALGO_RECENT, orderByImportance);
|
||||
}
|
||||
break;
|
||||
case 'MatrixActions.Room.receipt': {
|
||||
|
@ -191,7 +220,8 @@ class RoomListStore extends Store {
|
|||
if (!logicallyReady ||
|
||||
!payload.isLiveEvent ||
|
||||
!payload.isLiveUnfilteredRoomTimelineEvent ||
|
||||
!this._eventTriggersRecentReorder(payload.event)
|
||||
!this._eventTriggersRecentReorder(payload.event) ||
|
||||
this._state.algorithm !== ALGO_RECENT
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
@ -305,7 +335,7 @@ class RoomListStore extends Store {
|
|||
_filterTags(tags) {
|
||||
tags = tags ? Object.keys(tags) : [];
|
||||
if (this._state.tagsEnabled) return tags;
|
||||
return tags.filter((t) => !!LIST_ORDERS[t]);
|
||||
return tags.filter((t) => knownLists.has(t));
|
||||
}
|
||||
|
||||
_getRecommendedTagsForRoom(room) {
|
||||
|
@ -448,6 +478,12 @@ class RoomListStore extends Store {
|
|||
_setRoomCategory(room, category) {
|
||||
if (!room) return; // This should only happen in tests
|
||||
|
||||
if (!this._state.orderImportantFirst) {
|
||||
// XXX bail here early to avoid https://github.com/vector-im/riot-web/issues/9216
|
||||
// this may mean that category updates are missed whilst not ordering by importance first
|
||||
return;
|
||||
}
|
||||
|
||||
const listsClone = {};
|
||||
|
||||
// Micro optimization: Support lazily loading the last timestamp in a room
|
||||
|
@ -477,7 +513,7 @@ class RoomListStore extends Store {
|
|||
// Speed optimization: Don't do complicated math if we don't have to.
|
||||
if (!shouldHaveRoom) {
|
||||
listsClone[key] = this._state.lists[key].filter((e) => e.room.roomId !== room.roomId);
|
||||
} else if (LIST_ORDERS[key] !== 'recent') {
|
||||
} else if (getListAlgorithm(key, this._state.algorithm) === ALGO_MANUAL) {
|
||||
// Manually ordered tags are sorted later, so for now we'll just clone the tag
|
||||
// and add our room if needed
|
||||
listsClone[key] = this._state.lists[key].filter((e) => e.room.roomId !== room.roomId);
|
||||
|
@ -538,7 +574,7 @@ class RoomListStore extends Store {
|
|||
|
||||
// Sort the favourites before we set the clone
|
||||
for (const tag of Object.keys(listsClone)) {
|
||||
if (LIST_ORDERS[tag] === 'recent') continue; // skip recents (pre-sorted)
|
||||
if (getListAlgorithm(tag, this._state.algorithm) !== ALGO_MANUAL) continue; // skip recents (pre-sorted)
|
||||
listsClone[tag].sort(this._getManualComparator(tag));
|
||||
}
|
||||
|
||||
|
@ -588,8 +624,10 @@ class RoomListStore extends Store {
|
|||
|
||||
// Default to an arbitrary category for tags which aren't ordered by recents
|
||||
let category = CATEGORY_IDLE;
|
||||
if (LIST_ORDERS[tagName] === 'recent') category = this._calculateCategory(room);
|
||||
lists[tagName].push({room, category: category});
|
||||
if (getListAlgorithm(tagName, this._state.algorithm) !== ALGO_MANUAL) {
|
||||
category = this._calculateCategory(room);
|
||||
}
|
||||
lists[tagName].push({room, category});
|
||||
}
|
||||
} else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
|
||||
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
|
||||
|
@ -608,13 +646,7 @@ class RoomListStore extends Store {
|
|||
// cache only needs to survive the sort operation below and should not be implemented outside
|
||||
// of this function, otherwise the room lists will almost certainly be out of date and wrong.
|
||||
const latestEventTsCache = {}; // roomId => timestamp
|
||||
|
||||
Object.keys(lists).forEach((listKey) => {
|
||||
let comparator;
|
||||
switch (LIST_ORDERS[listKey]) {
|
||||
case "recent":
|
||||
comparator = (entryA, entryB) => {
|
||||
return this._recentsComparator(entryA, entryB, (room) => {
|
||||
const tsOfNewestEventFn = (room) => {
|
||||
if (!room) return Number.MAX_SAFE_INTEGER; // Should only happen in tests
|
||||
|
||||
if (latestEventTsCache[room.roomId]) {
|
||||
|
@ -624,15 +656,38 @@ class RoomListStore extends Store {
|
|||
const ts = this._tsOfNewestEvent(room);
|
||||
latestEventTsCache[room.roomId] = ts;
|
||||
return ts;
|
||||
});
|
||||
};
|
||||
|
||||
Object.keys(lists).forEach((listKey) => {
|
||||
let comparator;
|
||||
switch (getListAlgorithm(listKey, this._state.algorithm)) {
|
||||
case ALGO_RECENT:
|
||||
comparator = (entryA, entryB) => this._recentsComparator(entryA, entryB, tsOfNewestEventFn);
|
||||
break;
|
||||
case "manual":
|
||||
case ALGO_ALPHABETIC:
|
||||
comparator = this._lexicographicalComparator;
|
||||
break;
|
||||
case ALGO_MANUAL:
|
||||
default:
|
||||
comparator = this._getManualComparator(listKey);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this._state.orderImportantFirst) {
|
||||
lists[listKey].sort((entryA, entryB) => {
|
||||
if (entryA.category !== entryB.category) {
|
||||
const idxA = CATEGORY_ORDER.indexOf(entryA.category);
|
||||
const idxB = CATEGORY_ORDER.indexOf(entryB.category);
|
||||
if (idxA > idxB) return 1;
|
||||
if (idxA < idxB) return -1;
|
||||
return 0; // Technically not possible
|
||||
}
|
||||
return comparator(entryA, entryB);
|
||||
});
|
||||
} else {
|
||||
// skip the category comparison even though it should no-op when orderImportantFirst disabled
|
||||
lists[listKey].sort(comparator);
|
||||
}
|
||||
});
|
||||
|
||||
this._setState({
|
||||
|
@ -671,7 +726,7 @@ class RoomListStore extends Store {
|
|||
}
|
||||
|
||||
_calculateCategory(room) {
|
||||
if (!this._state.orderRoomsByImportance) {
|
||||
if (!this._state.orderImportantFirst) {
|
||||
// Effectively disable the categorization of rooms if we're supposed to
|
||||
// be sorting by more recent messages first. This triggers the timestamp
|
||||
// comparison bit of _setRoomCategory and _recentsComparator instead of
|
||||
|
@ -692,26 +747,13 @@ class RoomListStore extends Store {
|
|||
}
|
||||
|
||||
_recentsComparator(entryA, entryB, tsOfNewestEventFn) {
|
||||
const roomA = entryA.room;
|
||||
const roomB = entryB.room;
|
||||
const categoryA = entryA.category;
|
||||
const categoryB = entryB.category;
|
||||
|
||||
if (categoryA !== categoryB) {
|
||||
const idxA = CATEGORY_ORDER.indexOf(categoryA);
|
||||
const idxB = CATEGORY_ORDER.indexOf(categoryB);
|
||||
if (idxA > idxB) return 1;
|
||||
if (idxA < idxB) return -1;
|
||||
return 0; // Technically not possible
|
||||
}
|
||||
|
||||
const timestampA = tsOfNewestEventFn(roomA);
|
||||
const timestampB = tsOfNewestEventFn(roomB);
|
||||
const timestampA = tsOfNewestEventFn(entryA.room);
|
||||
const timestampB = tsOfNewestEventFn(entryB.room);
|
||||
return timestampB - timestampA;
|
||||
}
|
||||
|
||||
_lexicographicalComparator(roomA, roomB) {
|
||||
return roomA.name > roomB.name ? 1 : -1;
|
||||
_lexicographicalComparator(entryA, entryB) {
|
||||
return entryA.room.name.localeCompare(entryB.room.name);
|
||||
}
|
||||
|
||||
_getManualComparator(tagName, optimisticRequest) {
|
||||
|
@ -736,7 +778,7 @@ class RoomListStore extends Store {
|
|||
return -1;
|
||||
}
|
||||
|
||||
return a === b ? this._lexicographicalComparator(roomA, roomB) : (a > b ? 1 : -1);
|
||||
return a === b ? this._lexicographicalComparator(entryA, entryB) : (a > b ? 1 : -1);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue