|
| 1 | +# Room list sorting |
| 2 | + |
| 3 | +It's so complicated it needs its own README. |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +Legend: |
| 8 | +* Orange = External event. |
| 9 | +* Purple = Deterministic flow. |
| 10 | +* Green = Algorithm definition. |
| 11 | +* Red = Exit condition/point. |
| 12 | +* Blue = Process definition. |
| 13 | + |
| 14 | +## Algorithms involved |
| 15 | + |
| 16 | +There's two main kinds of algorithms involved in the room list store: list ordering and tag sorting. |
| 17 | +Throughout the code an intentional decision has been made to call them the List Algorithm and Sorting |
| 18 | +Algorithm respectively. The list algorithm determines the primary ordering of a given tag whereas the |
| 19 | +tag sorting defines how rooms within that tag get sorted, at the discretion of the list ordering. |
| 20 | + |
| 21 | +Behaviour of the overall room list (sticky rooms, etc) are determined by the generically-named Algorithm |
| 22 | +class. Here is where much of the coordination from the room list store is done to figure out which list |
| 23 | +algorithm to call, instead of having all the logic in the room list store itself. |
| 24 | + |
| 25 | + |
| 26 | +Tag sorting is effectively the comparator supplied to the list algorithm. This gives the list algorithm |
| 27 | +the power to decide when and how to apply the tag sorting, if at all. For example, the importance algorithm, |
| 28 | +later described in this document, heavily uses the list ordering behaviour to break the tag into categories. |
| 29 | +Each category then gets sorted by the appropriate tag sorting algorithm. |
| 30 | + |
| 31 | +### Tag sorting algorithm: Alphabetical |
| 32 | + |
| 33 | +When used, rooms in a given tag will be sorted alphabetically, where the alphabet's order is a problem |
| 34 | +for the browser. All we do is a simple string comparison and expect the browser to return something |
| 35 | +useful. |
| 36 | + |
| 37 | +### Tag sorting algorithm: Manual |
| 38 | + |
| 39 | +Manual sorting makes use of the `order` property present on all tags for a room, per the |
| 40 | +[Matrix specification](https://matrix.org/docs/spec/client_server/r0.6.0#room-tagging). Smaller values |
| 41 | +of `order` cause rooms to appear closer to the top of the list. |
| 42 | + |
| 43 | +### Tag sorting algorithm: Recent |
| 44 | + |
| 45 | +Rooms get ordered by the timestamp of the most recent useful message. Usefulness is yet another algorithm |
| 46 | +in the room list system which determines whether an event type is capable of bubbling up in the room list. |
| 47 | +Normally events like room messages, stickers, and room security changes will be considered useful enough |
| 48 | +to cause a shift in time. |
| 49 | + |
| 50 | +Note that this is reliant on the event timestamps of the most recent message. Because Matrix is eventually |
| 51 | +consistent this means that from time to time a room might plummet or skyrocket across the tag due to the |
| 52 | +timestamp contained within the event (generated server-side by the sender's server). |
| 53 | + |
| 54 | +### List ordering algorithm: Natural |
| 55 | + |
| 56 | +This is the easiest of the algorithms to understand because it does essentially nothing. It imposes no |
| 57 | +behavioural changes over the tag sorting algorithm and is by far the simplest way to order a room list. |
| 58 | +Historically, it's been the only option in Riot and extremely common in most chat applications due to |
| 59 | +its relative deterministic behaviour. |
| 60 | + |
| 61 | +### List ordering algorithm: Importance |
| 62 | + |
| 63 | +On the other end of the spectrum, this is the most complicated algorithm which exists. There's major |
| 64 | +behavioural changes, and the tag sorting algorithm gets selectively applied depending on circumstances. |
| 65 | + |
| 66 | +Each tag which is not manually ordered gets split into 4 sections or "categories". Manually ordered tags |
| 67 | +simply get the manual sorting algorithm applied to them with no further involvement from the importance |
| 68 | +algorithm. There are 4 categories: Red, Grey, Bold, and Idle. Each has their own definition based off |
| 69 | +relative (perceived) importance to the user: |
| 70 | + |
| 71 | +* **Red**: The room has unread mentions waiting for the user. |
| 72 | +* **Grey**: The room has unread notifications waiting for the user. Notifications are simply unread |
| 73 | + messages which cause a push notification or badge count. Typically, this is the default as rooms get |
| 74 | + set to 'All Messages'. |
| 75 | +* **Bold**: The room has unread messages waiting for the user. Essentially this is a grey room without |
| 76 | + a badge/notification count (or 'Mentions Only'/'Muted'). |
| 77 | +* **Idle**: No useful (see definition of useful above) activity has occurred in the room since the user |
| 78 | + last read it. |
| 79 | + |
| 80 | +Conveniently, each tag gets ordered by those categories as presented: red rooms appear above grey, grey |
| 81 | +above bold, etc. |
| 82 | + |
| 83 | +Once the algorithm has determined which rooms belong in which categories, the tag sorting algorithm |
| 84 | +gets applied to each category in a sub-list fashion. This should result in the red rooms (for example) |
| 85 | +being sorted alphabetically amongst each other as well as the grey rooms sorted amongst each other, but |
| 86 | +collectively the tag will be sorted into categories with red being at the top. |
| 87 | + |
| 88 | +## Sticky rooms |
| 89 | + |
| 90 | +When the user visits a room, that room becomes 'sticky' in the list, regardless of ordering algorithm. |
| 91 | +From a code perspective, the underlying algorithm is not aware of a sticky room and instead the base class |
| 92 | +manages which room is sticky. This is to ensure that all algorithms handle it the same. |
| 93 | + |
| 94 | +The sticky flag is simply to say it will not move higher or lower down the list while it is active. For |
| 95 | +example, if using the importance algorithm, the room would naturally become idle once viewed and thus |
| 96 | +would normally fly down the list out of sight. The sticky room concept instead holds it in place, never |
| 97 | +letting it fly down until the user moves to another room. |
| 98 | + |
| 99 | +Only one room can be sticky at a time. Room updates around the sticky room will still hold the sticky |
| 100 | +room in place. The best example of this is the importance algorithm: if the user has 3 red rooms and |
| 101 | +selects the middle room, they will see exactly one room above their selection at all times. If they |
| 102 | +receive another notification which causes the room to move into the topmost position, the room that was |
| 103 | +above the sticky room will move underneath to allow for the new room to take the top slot, maintaining |
| 104 | +the sticky room's position. |
| 105 | + |
| 106 | +Though only applicable to the importance algorithm, the sticky room is not aware of category boundaries |
| 107 | +and thus the user can see a shift in what kinds of rooms move around their selection. An example would |
| 108 | +be the user having 4 red rooms, the user selecting the third room (leaving 2 above it), and then having |
| 109 | +the rooms above it read on another device. This would result in 1 red room and 1 other kind of room |
| 110 | +above the sticky room as it will try to maintain 2 rooms above the sticky room. |
| 111 | + |
| 112 | +An exception for the sticky room placement is when there's suddenly not enough rooms to maintain the placement |
| 113 | +exactly. This typically happens if the user selects a room and leaves enough rooms where it cannot maintain |
| 114 | +the N required rooms above the sticky room. In this case, the sticky room will simply decrease N as needed. |
| 115 | +The N value will never increase while selection remains unchanged: adding a bunch of rooms after having |
| 116 | +put the sticky room in a position where it's had to decrease N will not increase N. |
| 117 | + |
| 118 | +## Responsibilities of the store |
| 119 | + |
| 120 | +The store is responsible for the ordering, upkeep, and tracking of all rooms. The room list component simply gets |
| 121 | +an object containing the tags it needs to worry about and the rooms within. The room list component will |
| 122 | +decide which tags need rendering (as it commonly filters out empty tags in most cases), and will deal with |
| 123 | +all kinds of filtering. |
| 124 | + |
| 125 | +## Filtering |
| 126 | + |
| 127 | +Filters are provided to the store as condition classes, which are then passed along to the algorithm |
| 128 | +implementations. The implementations then get to decide how to actually filter the rooms, however in |
| 129 | +practice the base `Algorithm` class deals with the filtering in a more optimized/generic way. |
| 130 | + |
| 131 | +The results of filters get cached to avoid needlessly iterating over potentially thousands of rooms, |
| 132 | +as the old room list store does. When a filter condition changes, it emits an update which (in this |
| 133 | +case) the `Algorithm` class will pick up and act accordingly. Typically, this also means filtering a |
| 134 | +minor subset where possible to avoid over-iterating rooms. |
| 135 | + |
| 136 | +All filter conditions are considered "stable" by the consumers, meaning that the consumer does not |
| 137 | +expect a change in the condition unless the condition says it has changed. This is intentional to |
| 138 | +maintain the caching behaviour described above. |
| 139 | + |
| 140 | +## Class breakdowns |
| 141 | + |
| 142 | +The `RoomListStore` is the major coordinator of various algorithm implementations, which take care |
| 143 | +of the various `ListAlgorithm` and `SortingAlgorithm` options. The `Algorithm` class is responsible |
| 144 | +for figuring out which tags get which rooms, as Matrix specifies them as a reverse map: tags get |
| 145 | +defined on rooms and are not defined as a collection of rooms (unlike how they are presented to the |
| 146 | +user). Various list-specific utilities are also included, though they are expected to move somewhere |
| 147 | +more general when needed. For example, the `membership` utilities could easily be moved elsewhere |
| 148 | +as needed. |
| 149 | + |
| 150 | +The various bits throughout the room list store should also have jsdoc of some kind to help describe |
| 151 | +what they do and how they work. |
0 commit comments