diff --git a/res/css/views/rooms/_NotificationBadge.scss b/res/css/views/rooms/_NotificationBadge.scss index 521f1dfc20..0e6d442cc1 100644 --- a/res/css/views/rooms/_NotificationBadge.scss +++ b/res/css/views/rooms/_NotificationBadge.scss @@ -48,15 +48,15 @@ limitations under the License. } &.mx_NotificationBadge_2char { - width: 16px; - height: 16px; - border-radius: 16px; + width: $font-16px; + height: $font-16px; + border-radius: $font-16px; } &.mx_NotificationBadge_3char { - width: 26px; - height: 16px; - border-radius: 16px; + width: $font-26px; + height: $font-16px; + border-radius: $font-16px; } // The following is the floating badge diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index c4638c583d..bec0952489 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -198,6 +198,8 @@ limitations under the License. // as the box model should be top aligned. Happens in both FF and Chromium display: flex; flex-direction: column; + + mask-image: linear-gradient(0deg, transparent, black 3px); } .mx_RoomSublist2_resizerHandles_showNButton { diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index c22e6cd807..2887b7fa55 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -137,9 +137,10 @@ export default class RoomSublist2 extends React.Component { let padding = RESIZE_HANDLE_HEIGHT; // this is used for calculating the max height of the whole container, // and takes into account whether there should be room reserved for the show less button - // when fully expanded. Note that the show more button might still be shown when not fully expanded, - // but in this case it will take the space of a tile and we don't need to reserve space for it. - if (this.numTiles > this.layout.defaultVisibleTiles) { + // when fully expanded. We cannot check against the layout's defaultVisible tile count + // because there are conditions in which we need to know that the 'show more' button + // is present while well under the default tile limit. + if (this.numTiles > this.numVisibleTiles) { padding += SHOW_N_BUTTON_HEIGHT; } return padding; diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 8686a3a054..d67c728bf0 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -30,10 +30,10 @@ import { TagWatcher } from "./TagWatcher"; import RoomViewStore from "../RoomViewStore"; import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm"; import { EffectiveMembership, getEffectiveMembership } from "./membership"; -import { ListLayout } from "./ListLayout"; import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; import RoomListLayoutStore from "./RoomListLayoutStore"; import { MarkedExecution } from "../../utils/MarkedExecution"; +import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; interface IState { tagsEnabled?: boolean; @@ -45,14 +45,13 @@ interface IState { */ export const LISTS_UPDATE_EVENT = "lists_update"; -export class RoomListStore2 extends AsyncStore { +export class RoomListStore2 extends AsyncStoreWithClient { /** * Set to true if you're running tests on the store. Should not be touched in * any other environment. */ public static TEST_MODE = false; - private _matrixClient: MatrixClient; private initialListsGenerated = false; private enabled = false; private algorithm = new Algorithm(); @@ -80,7 +79,7 @@ export class RoomListStore2 extends AsyncStore { } public get matrixClient(): MatrixClient { - return this._matrixClient; + return super.matrixClient; } // Intended for test usage @@ -89,23 +88,28 @@ export class RoomListStore2 extends AsyncStore { this.tagWatcher = new TagWatcher(this); this.filterConditions = []; this.initialListsGenerated = false; - this._matrixClient = null; this.algorithm.off(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); this.algorithm.off(FILTER_CHANGED, this.onAlgorithmListUpdated); this.algorithm = new Algorithm(); this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); this.algorithm.on(FILTER_CHANGED, this.onAlgorithmListUpdated); + + // Reset state without causing updates as the client will have been destroyed + // and downstream code will throw NPE errors. + await this.reset(null, true); } // Public for test usage. Do not call this. - public async makeReady(client: MatrixClient) { + public async makeReady(forcedClient?: MatrixClient) { + if (forcedClient) { + super.matrixClient = forcedClient; + } + // TODO: Remove with https://github.com/vector-im/riot-web/issues/14367 this.checkEnabled(); if (!this.enabled) return; - this._matrixClient = client; - // Update any settings here, as some may have happened before we were logically ready. // Update any settings here, as some may have happened before we were logically ready. console.log("Regenerating room lists: Startup"); @@ -162,7 +166,15 @@ export class RoomListStore2 extends AsyncStore { if (trigger) this.updateFn.trigger(); } - protected async onDispatch(payload: ActionPayload) { + protected async onReady(): Promise { + await this.makeReady(); + } + + protected async onNotReady(): Promise { + await this.resetStore(); + } + + protected async onAction(payload: ActionPayload) { // When we're running tests we can't reliably use setImmediate out of timing concerns. // As such, we use a more synchronous model. if (RoomListStore2.TEST_MODE) { @@ -176,29 +188,10 @@ export class RoomListStore2 extends AsyncStore { } protected async onDispatchAsync(payload: ActionPayload) { - if (payload.action === 'MatrixActions.sync') { - // Filter out anything that isn't the first PREPARED sync. - if (!(payload.prevState === 'PREPARED' && payload.state !== 'PREPARED')) { - return; - } - - await this.makeReady(payload.matrixClient); - - return; // no point in running the next conditions - they won't match - } - // TODO: Remove this once the RoomListStore becomes default if (!this.enabled) return; - if (payload.action === 'on_client_not_viable' || payload.action === 'on_logged_out') { - // Reset state without causing updates as the client will have been destroyed - // and downstream code will throw NPE errors. - await this.reset(null, true); - this._matrixClient = null; - this.initialListsGenerated = false; // we'll want to regenerate them - } - - // Everything below here requires a MatrixClient or some sort of logical readiness. + // Everything here requires a MatrixClient or some sort of logical readiness. const logicallyReady = this.matrixClient && this.initialListsGenerated; if (!logicallyReady) return; @@ -425,7 +418,8 @@ export class RoomListStore2 extends AsyncStore { // logic must match calculateListOrder private calculateTagSorting(tagId: TagID): SortAlgorithm { - const defaultSort = SortAlgorithm.Alphabetic; + const isDefaultRecent = tagId === DefaultTagID.Invite || tagId === DefaultTagID.DM; + const defaultSort = isDefaultRecent ? SortAlgorithm.Recent : SortAlgorithm.Alphabetic; const settingAlphabetical = SettingsStore.getValue("RoomList.orderAlphabetically", null, true); const definedSort = this.getTagSorting(tagId); const storedSort = this.getStoredTagSorting(tagId); diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index 17e8283c74..a8adaed4d7 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -698,8 +698,8 @@ export class Algorithm extends EventEmitter { } } + let didTagChange = false; if (cause === RoomUpdateCause.PossibleTagChange) { - let didTagChange = false; const oldTags = this.roomIdsToTags[room.roomId] || []; const newTags = this.getTagsForRoom(room); const diff = arrayDiff(oldTags, newTags); @@ -713,6 +713,11 @@ export class Algorithm extends EventEmitter { if (!algorithm) throw new Error(`No algorithm for ${rmTag}`); await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved); this.cachedRooms[rmTag] = algorithm.orderedRooms; + + // Later on we won't update the filtered rooms or sticky room for removed + // tags, so do so now. + this.recalculateFilteredRoomsForTag(rmTag); + this.recalculateStickyRoom(rmTag); } for (const addTag of diff.added) { if (!window.mx_QuietRoomListLogging) { @@ -812,7 +817,7 @@ export class Algorithm extends EventEmitter { return false; } - let changed = false; + let changed = didTagChange; for (const tag of tags) { const algorithm: OrderingAlgorithm = this.algorithms[tag]; if (!algorithm) throw new Error(`No algorithm for ${tag}`); diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index e7ca94ed95..154fd40b69 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -38,7 +38,11 @@ export class RecentAlgorithm implements IAlgorithm { // actually changed (probably needs to be done higher up?) then we could do an // insertion sort or similar on the limited set of changes. - const myUserId = MatrixClientPeg.get().getUserId(); + // TODO: Don't assume we're using the same client as the peg + let myUserId = ''; + if (MatrixClientPeg.get()) { + myUserId = MatrixClientPeg.get().getUserId(); + } const tsCache: { [roomId: string]: number } = {}; const getLastTs = (r: Room) => { @@ -68,7 +72,6 @@ export class RecentAlgorithm implements IAlgorithm { const ev = r.timeline[i]; if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?) - // TODO: Don't assume we're using the same client as the peg if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) { return ev.getTs(); }