diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index e88157c0aa..1c650c42c4 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -55,7 +55,11 @@ $tagPanelWidth: 56px; // only applies in this file, used for calculations flex-direction: column; .mx_LeftPanel2_userHeader { - padding: 12px 12px 20px; // 12px top, 12px sides, 20px bottom + /* 12px top, 12px sides, 20px bottom (using 13px bottom to account + * for internal whitespace in the breadcrumbs) + */ + padding: 12px 12px 13px; + flex-shrink: 0; // to convince safari's layout engine the flexbox is fine // Create another flexbox column for the rows to stack within display: flex; @@ -73,7 +77,20 @@ $tagPanelWidth: 56px; // only applies in this file, used for calculations width: 100%; overflow-y: hidden; overflow-x: scroll; - margin-top: 8px; + margin-top: 20px; + padding-bottom: 2px; + + &.mx_IndicatorScrollbar_leftOverflow { + mask-image: linear-gradient(90deg, transparent, black 10%); + } + + &.mx_IndicatorScrollbar_rightOverflow { + mask-image: linear-gradient(90deg, black, black 90%, transparent); + } + + &.mx_IndicatorScrollbar_rightOverflow.mx_IndicatorScrollbar_leftOverflow { + mask-image: linear-gradient(90deg, transparent, black 10%, black 90%, transparent); + } } } @@ -81,6 +98,8 @@ $tagPanelWidth: 56px; // only applies in this file, used for calculations margin-left: 12px; margin-right: 12px; + flex-shrink: 0; // to convince safari's layout engine the flexbox is fine + // Create a flexbox to organize the inputs display: flex; align-items: center; diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index ee4ca5d320..dff69b49fa 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -24,6 +24,8 @@ limitations under the License. margin-left: 8px; width: 100%; + flex-shrink: 0; // to convince safari's layout engine the flexbox is fine + .mx_RoomSublist2_headerContainer { // Create a flexbox to make alignment easy display: flex; @@ -181,7 +183,6 @@ limitations under the License. } .mx_RoomSublist2_resizeBox { - margin-bottom: 4px; // for the resize handle position: relative; // Create another flexbox column for the tiles @@ -189,55 +190,22 @@ limitations under the License. flex-direction: column; overflow: hidden; - .mx_RoomSublist2_placeholder { - height: 44px; // Height of a room tile plus margins + .mx_RoomSublist2_tiles { + flex: 1 0 0; + overflow: hidden; + // need this to be flex otherwise the overflow hidden from above + // sometimes vertically centers the clipped list ... no idea why it would do this + // as the box model should be top aligned. Happens in both FF and Chromium + display: flex; + flex-direction: column; } - .mx_RoomSublist2_showNButton { - cursor: pointer; - font-size: $font-13px; - line-height: $font-18px; - color: $roomtile2-preview-color; + .mx_RoomSublist2_resizerHandles_showNButton { + flex: 0 0 32px; + } - // Update the render() function for RoomSublist2 if these change - // Update the ListLayout class for minVisibleTiles if these change. - // - // At 24px high, 8px padding on the top and 4px padding on the bottom this equates to 0.73 of - // a tile due to how the padding calculations work. - height: 24px; - padding-top: 8px; - padding-bottom: 4px; - - // We force this to the bottom so it will overlap rooms as needed. - // We account for the space it takes up (24px) in the code through padding. - position: absolute; - bottom: 0; - left: 0; - right: 0; - - // We create a flexbox to cheat at alignment - display: flex; - align-items: center; - - .mx_RoomSublist2_showNButtonChevron { - position: relative; - width: 16px; - height: 16px; - margin-left: 12px; - margin-right: 18px; - mask-position: center; - mask-size: contain; - mask-repeat: no-repeat; - background: $roomtile2-preview-color; - } - - .mx_RoomSublist2_showMoreButtonChevron { - mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); - } - - .mx_RoomSublist2_showLessButtonChevron { - mask-image: url('$(res)/img/feather-customised/chevron-up.svg'); - } + .mx_RoomSublist2_resizerHandles { + flex: 0 0 4px; } // Class name comes from the ResizableBox component @@ -269,6 +237,42 @@ limitations under the License. } } + .mx_RoomSublist2_showNButton { + cursor: pointer; + font-size: $font-13px; + line-height: $font-18px; + color: $roomtile2-preview-color; + + // Update the render() function for RoomSublist2 if these change + // Update the ListLayout class for minVisibleTiles if these change. + height: 24px; + padding-bottom: 4px; + + // We create a flexbox to cheat at alignment + display: flex; + align-items: center; + + .mx_RoomSublist2_showNButtonChevron { + position: relative; + width: 16px; + height: 16px; + margin-left: 12px; + margin-right: 18px; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $roomtile2-preview-color; + } + + .mx_RoomSublist2_showMoreButtonChevron { + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); + } + + .mx_RoomSublist2_showLessButtonChevron { + mask-image: url('$(res)/img/feather-customised/chevron-up.svg'); + } + } + &.mx_RoomSublist2_hasMenuOpen, &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer:focus-within, &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer:hover { @@ -314,13 +318,13 @@ limitations under the License. .mx_RoomSublist2_resizeBox { align-items: center; + } - .mx_RoomSublist2_showNButton { - flex-direction: column; + .mx_RoomSublist2_showNButton { + flex-direction: column; - .mx_RoomSublist2_showNButtonChevron { - margin-right: 12px; // to center - } + .mx_RoomSublist2_showNButtonChevron { + margin-right: 12px; // to center } } diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index fc52296d8b..3f970ea8c3 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -37,6 +37,9 @@ declare global { mx_RoomListStore2: RoomListStore2; mx_RoomListLayoutStore: RoomListLayoutStore; mxPlatformPeg: PlatformPeg; + + // TODO: Remove flag before launch: https://github.com/vector-im/riot-web/issues/14231 + mx_QuietRoomListLogging: boolean; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index ba0464a0a4..440b0d5bb3 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -219,7 +219,10 @@ export default class RoomList2 extends React.Component { private updateLists = () => { const newLists = RoomListStore.instance.orderedLists; - console.log("new lists", newLists); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log("new lists", newLists); + } this.setState({sublists: newLists}, () => { this.props.onResize(); diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 67a84e9292..bfbdd3a161 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -59,7 +59,7 @@ import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore"; * warning disappears. * *******************************************************************/ -const SHOW_N_BUTTON_HEIGHT = 32; // As defined by CSS +const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS export const HEADER_HEIGHT = 32; // As defined by CSS @@ -87,6 +87,12 @@ interface IProps { // TODO: Account for https://github.com/vector-im/riot-web/issues/14179 } +// TODO: Use re-resizer's NumberSize when it is exposed as the type +interface ResizeDelta { + width: number; + height: number; +} + type PartialDOMRect = Pick; interface IState { @@ -94,6 +100,7 @@ interface IState { contextMenuPosition: PartialDOMRect; isResizing: boolean; isExpanded: boolean; // used for the for expand of the sublist when the room list is being filtered + height: number; } export default class RoomSublist2 extends React.Component { @@ -101,28 +108,54 @@ export default class RoomSublist2 extends React.Component { private sublistRef = createRef(); private dispatcherRef: string; private layout: ListLayout; + private heightAtStart: number; constructor(props: IProps) { super(props); this.layout = RoomListLayoutStore.instance.getLayoutFor(this.props.tagId); - + this.heightAtStart = 0; + const height = this.calculateInitialHeight(); this.state = { notificationState: RoomNotificationStateStore.instance.getListState(this.props.tagId), contextMenuPosition: null, isResizing: false, - isExpanded: this.props.isFiltered ? this.props.isFiltered : !this.layout.isCollapsed + isExpanded: this.props.isFiltered ? this.props.isFiltered : !this.layout.isCollapsed, + height, }; this.state.notificationState.setRooms(this.props.rooms); this.dispatcherRef = defaultDispatcher.register(this.onAction); } + private calculateInitialHeight() { + const requestedVisibleTiles = Math.max(Math.floor(this.layout.visibleTiles), this.layout.minVisibleTiles); + const tileCount = Math.min(this.numTiles, requestedVisibleTiles); + const height = this.layout.tilesToPixelsWithPadding(tileCount, this.padding); + return height; + } + + private get padding() { + 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) { + padding += SHOW_N_BUTTON_HEIGHT; + } + return padding; + } + private get numTiles(): number { - return (this.props.rooms || []).length + (this.props.extraBadTilesThatShouldntExist || []).length; + return RoomSublist2.calcNumTiles(this.props); + } + + private static calcNumTiles(props) { + return (props.rooms || []).length + (props.extraBadTilesThatShouldntExist || []).length; } private get numVisibleTiles(): number { - const nVisible = Math.floor(this.layout.visibleTiles); + const nVisible = Math.ceil(this.layout.visibleTiles); return Math.min(nVisible, this.numTiles); } @@ -135,6 +168,11 @@ export default class RoomSublist2 extends React.Component { this.setState({isExpanded: !this.layout.isCollapsed}); } } + // as the rooms can come in one by one we need to reevaluate + // the amount of available rooms to cap the amount of requested visible rooms by the layout + if (RoomSublist2.calcNumTiles(prevProps) !== this.numTiles) { + this.setState({height: this.calculateInitialHeight()}); + } } public componentWillUnmount() { @@ -166,47 +204,50 @@ export default class RoomSublist2 extends React.Component { if (this.props.onAddRoom) this.props.onAddRoom(); }; + private applyHeightChange(newHeight: number) { + const heightInTiles = Math.ceil(this.layout.pixelsToTiles(newHeight - this.padding)); + this.layout.visibleTiles = Math.min(this.numTiles, heightInTiles); + } + private onResize = ( e: MouseEvent | TouchEvent, travelDirection: Direction, refToElement: HTMLDivElement, - delta: { width: number, height: number }, // TODO: Use re-resizer's NumberSize when it is exposed as the type + delta: ResizeDelta, ) => { - // Do some sanity checks, but in reality we shouldn't need these. - if (travelDirection !== "bottom") return; - if (delta.height === 0) return; // something went wrong, so just ignore it. - - // NOTE: the movement in the MouseEvent (not present on a TouchEvent) is inaccurate - // for our purposes. The delta provided by the library is also a change *from when - // resizing started*, meaning it is fairly useless for us. This is why we just use - // the client height and run with it. - - const heightBefore = this.layout.visibleTiles; - const heightInTiles = this.layout.pixelsToTiles(refToElement.clientHeight); - this.layout.setVisibleTilesWithin(heightInTiles, this.numTiles); - if (heightBefore === this.layout.visibleTiles) return; // no-op - this.forceUpdate(); // because the layout doesn't trigger a re-render + const newHeight = this.heightAtStart + delta.height; + this.applyHeightChange(newHeight); + this.setState({height: newHeight}); }; private onResizeStart = () => { + this.heightAtStart = this.state.height; this.setState({isResizing: true}); }; - private onResizeStop = () => { - this.setState({isResizing: false}); + private onResizeStop = ( + e: MouseEvent | TouchEvent, + travelDirection: Direction, + refToElement: HTMLDivElement, + delta: ResizeDelta, + ) => { + const newHeight = this.heightAtStart + delta.height; + this.applyHeightChange(newHeight); + this.setState({isResizing: false, height: newHeight}); }; private onShowAllClick = () => { - const numVisibleTiles = this.numVisibleTiles; - this.layout.visibleTiles = this.layout.tilesWithPadding(this.numTiles, MAX_PADDING_HEIGHT); - this.forceUpdate(); // because the layout doesn't trigger a re-render - setImmediate(this.focusRoomTile, numVisibleTiles); // focus the tile after the current bottom one + const newHeight = this.layout.tilesToPixelsWithPadding(this.numTiles, this.padding); + this.applyHeightChange(newHeight); + this.setState({height: newHeight}, () => { + this.focusRoomTile(this.numTiles - 1); + }); }; private onShowLessClick = () => { - this.layout.visibleTiles = this.layout.defaultVisibleTiles; - this.forceUpdate(); // because the layout doesn't trigger a re-render - // focus will flow to the show more button here + const newHeight = this.layout.tilesToPixelsWithPadding(this.layout.defaultVisibleTiles, this.padding); + this.applyHeightChange(newHeight); + this.setState({height: newHeight}); }; private focusRoomTile = (index: number) => { @@ -559,7 +600,6 @@ export default class RoomSublist2 extends React.Component { // TODO: Error boundary: https://github.com/vector-im/riot-web/issues/14185 const visibleTiles = this.renderVisibleTiles(); - const classes = classNames({ 'mx_RoomSublist2': true, 'mx_RoomSublist2_hasMenuOpen': !!this.state.contextMenuPosition, @@ -570,6 +610,11 @@ export default class RoomSublist2 extends React.Component { if (visibleTiles.length > 0) { const layout = this.layout; // to shorten calls + const minTiles = Math.min(layout.minVisibleTiles, this.numTiles); + const showMoreAtMinHeight = minTiles < this.numTiles; + const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0); + const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding); + const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding); const showMoreBtnClasses = classNames({ 'mx_RoomSublist2_showNButton': true, }); @@ -578,9 +623,11 @@ export default class RoomSublist2 extends React.Component { // floats above the resize handle, if we have one present. If the user has all // tiles visible, it becomes 'show less'. let showNButton = null; - if (this.numTiles > visibleTiles.length) { - // we have a cutoff condition - add the button to show all - const numMissing = this.numTiles - visibleTiles.length; + + if (maxTilesPx > this.state.height) { + const nonPaddedHeight = this.state.height - RESIZE_HANDLE_HEIGHT - SHOW_N_BUTTON_HEIGHT; + const amountFullyShown = Math.floor(nonPaddedHeight / this.layout.tileHeight); + const numMissing = this.numTiles - amountFullyShown; let showMoreText = ( {_t("Show %(count)s more", {count: numMissing})} @@ -595,7 +642,7 @@ export default class RoomSublist2 extends React.Component { {showMoreText} ); - } else if (this.numTiles <= visibleTiles.length && this.numTiles > this.layout.defaultVisibleTiles) { + } else if (this.numTiles > this.layout.defaultVisibleTiles) { // we have all tiles visible - add a button to show less let showLessText = ( @@ -639,44 +686,31 @@ export default class RoomSublist2 extends React.Component { // goes backwards and can become wildly incorrect (visibleTiles says 18 when there's // only mathematically 7 possible). - // The padding is variable though, so figure out what we need padding for. - let padding = 0; - if (showNButton) padding += SHOW_N_BUTTON_HEIGHT; - padding += RESIZE_HANDLE_HEIGHT; // always append the handle height + const handleWrapperClasses = classNames({ + 'mx_RoomSublist2_resizerHandles': true, + 'mx_RoomSublist2_resizerHandles_showNButton': !!showNButton, + }); - const relativeTiles = layout.tilesWithPadding(this.numTiles, padding); - const minTilesPx = layout.calculateTilesToPixelsMin(relativeTiles, layout.minVisibleTiles, padding); - const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, padding); - const tilesWithoutPadding = Math.min(relativeTiles, layout.visibleTiles); - const tilesPx = layout.calculateTilesToPixelsMin(relativeTiles, tilesWithoutPadding, padding); - - // Now that we know our padding constraints, let's find out if we need to chop off the - // last rendered visible tile so it doesn't collide with the 'show more' button - let visibleUnpaddedTiles = Math.round(layout.visibleTiles - layout.pixelsToTiles(padding)); - if (visibleUnpaddedTiles === visibleTiles.length - 1) { - const placeholder =
; - visibleTiles.splice(visibleUnpaddedTiles, 1, placeholder); - } - - const dimensions = { - height: tilesPx, - }; content = ( - - {visibleTiles} - {showNButton} - + + +
+ {visibleTiles} +
+ {showNButton} +
+
); } diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index db8084baa2..a7dc983fa6 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -256,7 +256,7 @@ export default class RoomTile2 extends React.Component { 0 )); } else { - console.log(`Unexpected tag ${tagId} applied to ${this.props.room.room_id}`); + console.warn(`Unexpected tag ${tagId} applied to ${this.props.room.room_id}`); } if ((ev as React.KeyboardEvent).key === Key.ENTER) { diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts index 5169c5e4e5..f1900487bc 100644 --- a/src/stores/room-list/ListLayout.ts +++ b/src/stores/room-list/ListLayout.ts @@ -20,7 +20,8 @@ const TILE_HEIGHT_PX = 44; // this comes from the CSS where the show more button is // mathematically this percent of a tile when floating. -const RESIZER_BOX_FACTOR = 0.78; +//const RESIZER_BOX_FACTOR = 0.78; +const RESIZER_BOX_FACTOR = 0; interface ISerializedListLayout { numTiles: number; @@ -109,6 +110,10 @@ export class ListLayout { return this.tilesToPixels(Math.min(maxTiles, n)) + padding; } + public tilesWithResizerBoxFactor(n: number): number { + return n + RESIZER_BOX_FACTOR; + } + public tilesWithPadding(n: number, paddingPx: number): number { return this.pixelsToTiles(this.tilesToPixelsWithPadding(n, paddingPx)); } diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 6020e46a12..05f678160e 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -33,6 +33,7 @@ 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"; interface IState { tagsEnabled?: boolean; @@ -51,7 +52,7 @@ export class RoomListStore2 extends AsyncStore { private algorithm = new Algorithm(); private filterConditions: IFilterCondition[] = []; private tagWatcher = new TagWatcher(this); - private layoutMap: Map = new Map(); + private updateFn = new MarkedExecution(() => this.emit(LISTS_UPDATE_EVENT)); private readonly watchedSettings = [ 'feature_custom_tags', @@ -62,7 +63,7 @@ export class RoomListStore2 extends AsyncStore { this.checkEnabled(); for (const settingName of this.watchedSettings) SettingsStore.monitorSetting(settingName, null); - RoomViewStore.addListener(this.onRVSUpdate); + RoomViewStore.addListener(() => this.handleRVSUpdate({})); this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); } @@ -91,27 +92,42 @@ export class RoomListStore2 extends AsyncStore { await this.updateAlgorithmInstances(); } - private onRVSUpdate = () => { + /** + * Handles suspected RoomViewStore changes. + * @param trigger Set to false to prevent a list update from being sent. Should only + * be used if the calling code will manually trigger the update. + */ + private async handleRVSUpdate({trigger = true}) { if (!this.enabled) return; // TODO: Remove with https://github.com/vector-im/riot-web/issues/14231 if (!this.matrixClient) return; // We assume there won't be RVS updates without a client const activeRoomId = RoomViewStore.getRoomId(); if (!activeRoomId && this.algorithm.stickyRoom) { - this.algorithm.stickyRoom = null; + await this.algorithm.setStickyRoom(null); } else if (activeRoomId) { const activeRoom = this.matrixClient.getRoom(activeRoomId); if (!activeRoom) { console.warn(`${activeRoomId} is current in RVS but missing from client - clearing sticky room`); - this.algorithm.stickyRoom = null; + await this.algorithm.setStickyRoom(null); } else if (activeRoom !== this.algorithm.stickyRoom) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Changing sticky room to ${activeRoomId}`); - this.algorithm.stickyRoom = activeRoom; + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Changing sticky room to ${activeRoomId}`); + } + await this.algorithm.setStickyRoom(activeRoom); } } - }; - protected async onDispatch(payload: ActionPayload) { + if (trigger) this.updateFn.trigger(); + } + + protected onDispatch(payload: ActionPayload) { + // We do this to intentionally break out of the current event loop task, allowing + // us to instead wait for a more convenient time to run our updates. + setImmediate(() => this.onDispatchAsync(payload)); + } + + 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')) { @@ -127,8 +143,12 @@ export class RoomListStore2 extends AsyncStore { // Update any settings here, as some may have happened before we were logically ready. console.log("Regenerating room lists: Startup"); await this.readAndCacheSettingsFromStore(); - await this.regenerateAllLists(); - this.onRVSUpdate(); // fake an RVS update to adjust sticky room, if needed + await this.regenerateAllLists({trigger: false}); + await this.handleRVSUpdate({trigger: false}); // fake an RVS update to adjust sticky room, if needed + + this.updateFn.trigger(); + + return; // no point in running the next conditions - they won't match } // TODO: Remove this once the RoomListStore becomes default @@ -137,7 +157,7 @@ export class RoomListStore2 extends AsyncStore { 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. - this.reset(null, true); + await this.reset(null, true); this._matrixClient = null; this.initialListsGenerated = false; // we'll want to regenerate them } @@ -151,7 +171,8 @@ export class RoomListStore2 extends AsyncStore { console.log("Regenerating room lists: Settings changed"); await this.readAndCacheSettingsFromStore(); - await this.regenerateAllLists(); // regenerate the lists now + await this.regenerateAllLists({trigger: false}); // regenerate the lists now + this.updateFn.trigger(); } } @@ -169,16 +190,22 @@ export class RoomListStore2 extends AsyncStore { console.warn(`Own read receipt was in unknown room ${room.roomId}`); return; } - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Got own read receipt in ${room.roomId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Got own read receipt in ${room.roomId}`); + } await this.handleRoomUpdate(room, RoomUpdateCause.ReadReceipt); + this.updateFn.trigger(); return; } } else if (payload.action === 'MatrixActions.Room.tags') { const roomPayload = (payload); // TODO: Type out the dispatcher types - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Got tag change in ${roomPayload.room.roomId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Got tag change in ${roomPayload.room.roomId}`); + } await this.handleRoomUpdate(roomPayload.room, RoomUpdateCause.PossibleTagChange); + this.updateFn.trigger(); } else if (payload.action === 'MatrixActions.Room.timeline') { const eventPayload = (payload); // TODO: Type out the dispatcher types @@ -188,12 +215,16 @@ export class RoomListStore2 extends AsyncStore { const roomId = eventPayload.event.getRoomId(); const room = this.matrixClient.getRoom(roomId); const tryUpdate = async (updatedRoom: Room) => { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()}` + - ` in ${updatedRoom.roomId}`); - if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') { + if (!window.mx_QuietRoomListLogging) { // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Got tombstone event - trying to remove now-dead room`); + console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()}` + + ` in ${updatedRoom.roomId}`); + } + if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') { + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Got tombstone event - trying to remove now-dead room`); + } const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()['replacement_room']); if (newRoom) { // If we have the new room, then the new room check will have seen the predecessor @@ -202,6 +233,7 @@ export class RoomListStore2 extends AsyncStore { } } await this.handleRoomUpdate(updatedRoom, RoomUpdateCause.Timeline); + this.updateFn.trigger(); }; if (!room) { console.warn(`Live timeline event ${eventPayload.event.getId()} received without associated room`); @@ -222,13 +254,18 @@ export class RoomListStore2 extends AsyncStore { console.warn(`Event ${eventPayload.event.getId()} was decrypted in an unknown room ${roomId}`); return; } - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Decrypted timeline event ${eventPayload.event.getId()} in ${roomId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Decrypted timeline event ${eventPayload.event.getId()} in ${roomId}`); + } await this.handleRoomUpdate(room, RoomUpdateCause.Timeline); + this.updateFn.trigger(); } else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') { const eventPayload = (payload); // TODO: Type out the dispatcher types - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Received updated DM map`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Received updated DM map`); + } const dmMap = eventPayload.event.getContent(); for (const userId of Object.keys(dmMap)) { const roomIds = dmMap[userId]; @@ -246,51 +283,73 @@ export class RoomListStore2 extends AsyncStore { await this.handleRoomUpdate(room, RoomUpdateCause.PossibleTagChange); } } + this.updateFn.trigger(); } else if (payload.action === 'MatrixActions.Room.myMembership') { const membershipPayload = (payload); // TODO: Type out the dispatcher types const oldMembership = getEffectiveMembership(membershipPayload.oldMembership); const newMembership = getEffectiveMembership(membershipPayload.membership); if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`); + } // If we're joining an upgraded room, we'll want to make sure we don't proliferate // the dead room in the list. const createEvent = membershipPayload.room.currentState.getStateEvents("m.room.create", ""); if (createEvent && createEvent.getContent()['predecessor']) { - console.log(`[RoomListDebug] Room has a predecessor`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Room has a predecessor`); + } const prevRoom = this.matrixClient.getRoom(createEvent.getContent()['predecessor']['room_id']); if (prevRoom) { const isSticky = this.algorithm.stickyRoom === prevRoom; if (isSticky) { - console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`); - await this.algorithm.setStickyRoomAsync(null); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`); + } + await this.algorithm.setStickyRoom(null); } // Note: we hit the algorithm instead of our handleRoomUpdate() function to // avoid redundant updates. - console.log(`[RoomListDebug] Removing previous room from room list`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Removing previous room from room list`); + } await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved); } } - console.log(`[RoomListDebug] Adding new room to room list`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Adding new room to room list`); + } await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom); + this.updateFn.trigger(); return; } if (oldMembership !== EffectiveMembership.Invite && newMembership === EffectiveMembership.Invite) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Handling invite to ${membershipPayload.room.roomId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Handling invite to ${membershipPayload.room.roomId}`); + } await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom); + this.updateFn.trigger(); return; } // If it's not a join, it's transitioning into a different list (possibly historical) if (oldMembership !== newMembership) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`); + } await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange); + this.updateFn.trigger(); return; } } @@ -299,9 +358,11 @@ export class RoomListStore2 extends AsyncStore { private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause); if (shouldUpdate) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[DEBUG] Room "${room.name}" (${room.roomId}) triggered by ${cause} requires list update`); - this.emit(LISTS_UPDATE_EVENT, this); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[DEBUG] Room "${room.name}" (${room.roomId}) triggered by ${cause} requires list update`); + } + this.updateFn.mark(); } } @@ -309,6 +370,7 @@ export class RoomListStore2 extends AsyncStore { await this.algorithm.setTagSorting(tagId, sort); // TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114 localStorage.setItem(`mx_tagSort_${tagId}`, sort); + this.updateFn.triggerIfWillMark(); } public getTagSorting(tagId: TagID): SortAlgorithm { @@ -347,6 +409,7 @@ export class RoomListStore2 extends AsyncStore { await this.algorithm.setListOrdering(tagId, order); // TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114 localStorage.setItem(`mx_listOrder_${tagId}`, order); + this.updateFn.triggerIfWillMark(); } public getListOrder(tagId: TagID): ListAlgorithm { @@ -382,6 +445,10 @@ export class RoomListStore2 extends AsyncStore { } private async updateAlgorithmInstances() { + // We'll require an update, so mark for one. Marking now also prevents the calls + // to setTagSorting and setListOrder from causing triggers. + this.updateFn.mark(); + for (const tag of Object.keys(this.orderedLists)) { const definedSort = this.getTagSorting(tag); const definedOrder = this.getListOrder(tag); @@ -405,12 +472,19 @@ export class RoomListStore2 extends AsyncStore { } private onAlgorithmListUpdated = () => { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log("Underlying algorithm has triggered a list update - refiring"); - this.emit(LISTS_UPDATE_EVENT, this); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log("Underlying algorithm has triggered a list update - marking"); + } + this.updateFn.mark(); }; - private async regenerateAllLists() { + /** + * Regenerates the room whole room list, discarding any previous results. + * @param trigger Set to false to prevent a list update from being sent. Should only + * be used if the calling code will manually trigger the update. + */ + private async regenerateAllLists({trigger = true}) { console.warn("Regenerating all room lists"); const sorts: ITagSortingMap = {}; @@ -435,21 +509,26 @@ export class RoomListStore2 extends AsyncStore { this.initialListsGenerated = true; - this.emit(LISTS_UPDATE_EVENT, this); + if (trigger) this.updateFn.trigger(); } public addFilter(filter: IFilterCondition): void { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log("Adding filter condition:", filter); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log("Adding filter condition:", filter); + } this.filterConditions.push(filter); if (this.algorithm) { this.algorithm.addFilterCondition(filter); } + this.updateFn.trigger(); } public removeFilter(filter: IFilterCondition): void { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log("Removing filter condition:", filter); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log("Removing filter condition:", filter); + } const idx = this.filterConditions.indexOf(filter); if (idx >= 0) { this.filterConditions.splice(idx, 1); @@ -458,6 +537,7 @@ export class RoomListStore2 extends AsyncStore { this.algorithm.removeFilterCondition(filter); } } + this.updateFn.trigger(); } /** diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index 35511a461d..d985abd392 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -87,12 +87,6 @@ export class Algorithm extends EventEmitter { return this._stickyRoom ? this._stickyRoom.room : null; } - public set stickyRoom(val: Room) { - // setters can't be async, so we call a private function to do the work - // noinspection JSIgnoredPromiseFromCall - this.updateStickyRoom(val); - } - protected get hasFilters(): boolean { return this.allowedByFilter.size > 0; } @@ -115,7 +109,7 @@ export class Algorithm extends EventEmitter { * Awaitable version of the sticky room setter. * @param val The new room to sticky. */ - public async setStickyRoomAsync(val: Room) { + public async setStickyRoom(val: Room) { await this.updateStickyRoom(val); } @@ -321,8 +315,10 @@ export class Algorithm extends EventEmitter { } newMap[tagId] = allowedRoomsInThisTag; - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[DEBUG] ${newMap[tagId].length}/${rooms.length} rooms filtered into ${tagId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[DEBUG] ${newMap[tagId].length}/${rooms.length} rooms filtered into ${tagId}`); + } } const allowedRooms = Object.values(newMap).reduce((rv, v) => { rv.push(...v); return rv; }, []); @@ -331,26 +327,13 @@ export class Algorithm extends EventEmitter { this.emit(LIST_UPDATED_EVENT); } - // TODO: Remove or use. - protected addPossiblyFilteredRoomsToTag(tagId: TagID, added: Room[]): void { - const filters = this.allowedByFilter.keys(); - for (const room of added) { - for (const filter of filters) { - if (filter.isVisible(room)) { - this.allowedRoomsByFilters.add(room); - break; - } - } - } - - // Now that we've updated the allowed rooms, recalculate the tag - this.recalculateFilteredRoomsForTag(tagId); - } - protected recalculateFilteredRoomsForTag(tagId: TagID): void { if (!this.hasFilters) return; // don't bother doing work if there's nothing to do - console.log(`Recalculating filtered rooms for ${tagId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Recalculating filtered rooms for ${tagId}`); + } delete this.filteredRooms[tagId]; const rooms = this.cachedRooms[tagId].map(r => r); // cheap clone this.tryInsertStickyRoomToFilterSet(rooms, tagId); @@ -359,8 +342,10 @@ export class Algorithm extends EventEmitter { this.filteredRooms[tagId] = filteredRooms; } - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[DEBUG] ${filteredRooms.length}/${rooms.length} rooms filtered into ${tagId}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[DEBUG] ${filteredRooms.length}/${rooms.length} rooms filtered into ${tagId}`); + } } protected tryInsertStickyRoomToFilterSet(rooms: Room[], tagId: TagID) { @@ -399,8 +384,10 @@ export class Algorithm extends EventEmitter { } if (!this._cachedStickyRooms || !updatedTag) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Generating clone of cached rooms for sticky room handling`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Generating clone of cached rooms for sticky room handling`); + } const stickiedTagMap: ITagMap = {}; for (const tagId of Object.keys(this.cachedRooms)) { stickiedTagMap[tagId] = this.cachedRooms[tagId].map(r => r); // shallow clone @@ -411,8 +398,10 @@ export class Algorithm extends EventEmitter { if (updatedTag) { // Update the tag indicated by the caller, if possible. This is mostly to ensure // our cache is up to date. - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Replacing cached sticky rooms for ${updatedTag}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Replacing cached sticky rooms for ${updatedTag}`); + } this._cachedStickyRooms[updatedTag] = this.cachedRooms[updatedTag].map(r => r); // shallow clone } @@ -421,8 +410,10 @@ export class Algorithm extends EventEmitter { // we might have updated from the cache is also our sticky room. const sticky = this._stickyRoom; if (!updatedTag || updatedTag === sticky.tag) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Inserting sticky room ${sticky.room.roomId} at position ${sticky.position} in ${sticky.tag}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Inserting sticky room ${sticky.room.roomId} at position ${sticky.position} in ${sticky.tag}`); + } this._cachedStickyRooms[sticky.tag].splice(sticky.position, 0, sticky.room); } @@ -647,8 +638,10 @@ export class Algorithm extends EventEmitter { * processing. */ public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Handle room update for ${room.roomId} called with cause ${cause}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Handle room update for ${room.roomId} called with cause ${cause}`); + } if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from"); // Note: check the isSticky against the room ID just in case the reference is wrong @@ -705,16 +698,20 @@ export class Algorithm extends EventEmitter { const diff = arrayDiff(oldTags, newTags); if (diff.removed.length > 0 || diff.added.length > 0) { for (const rmTag of diff.removed) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Removing ${room.roomId} from ${rmTag}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Removing ${room.roomId} from ${rmTag}`); + } const algorithm: OrderingAlgorithm = this.algorithms[rmTag]; if (!algorithm) throw new Error(`No algorithm for ${rmTag}`); await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved); this.cachedRooms[rmTag] = algorithm.orderedRooms; } for (const addTag of diff.added) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Adding ${room.roomId} to ${addTag}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Adding ${room.roomId} to ${addTag}`); + } const algorithm: OrderingAlgorithm = this.algorithms[addTag]; if (!algorithm) throw new Error(`No algorithm for ${addTag}`); await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom); @@ -724,13 +721,17 @@ export class Algorithm extends EventEmitter { // Update the tag map so we don't regen it in a moment this.roomIdsToTags[room.roomId] = newTags; - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`Changing update cause for ${room.roomId} to Timeline to sort rooms`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Changing update cause for ${room.roomId} to Timeline to sort rooms`); + } cause = RoomUpdateCause.Timeline; didTagChange = true; } else { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.warn(`Received no-op update for ${room.roomId} - changing to Timeline update`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`Received no-op update for ${room.roomId} - changing to Timeline update`); + } cause = RoomUpdateCause.Timeline; } @@ -746,7 +747,7 @@ export class Algorithm extends EventEmitter { }; } else { // We have to clear the lock as the sticky room change will trigger updates. - await this.setStickyRoomAsync(room); + await this.setStickyRoom(room); } } } @@ -756,20 +757,27 @@ export class Algorithm extends EventEmitter { // as the sticky room relies on this. if (cause !== RoomUpdateCause.NewRoom && cause !== RoomUpdateCause.RoomRemoved) { if (this.stickyRoom === room) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.warn(`[RoomListDebug] Received ${cause} update for sticky room ${room.roomId} - ignoring`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.warn(`[RoomListDebug] Received ${cause} update for sticky room ${room.roomId} - ignoring`); + } return false; } } if (!this.roomIdsToTags[room.roomId]) { if (CAUSES_REQUIRING_ROOM.includes(cause)) { - console.warn(`Skipping tag update for ${room.roomId} because we don't know about the room`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.warn(`Skipping tag update for ${room.roomId} because we don't know about the room`); + } return false; } - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Updating tags for room ${room.roomId} (${room.name})`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Updating tags for room ${room.roomId} (${room.name})`); + } // Get the tags for the room and populate the cache const roomTags = this.getTagsForRoom(room).filter(t => !isNullOrUndefined(this.cachedRooms[t])); @@ -780,12 +788,16 @@ export class Algorithm extends EventEmitter { this.roomIdsToTags[room.roomId] = roomTags; - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Updated tags for ${room.roomId}:`, roomTags); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Updated tags for ${room.roomId}:`, roomTags); + } } - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Reached algorithmic handling for ${room.roomId} and cause ${cause}`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Reached algorithmic handling for ${room.roomId} and cause ${cause}`); + } const tags = this.roomIdsToTags[room.roomId]; if (!tags) { @@ -807,8 +819,10 @@ export class Algorithm extends EventEmitter { changed = true; } - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Finished handling ${room.roomId} with cause ${cause} (changed=${changed})`); + if (!window.mx_QuietRoomListLogging) { + // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 + console.log(`[RoomListDebug] Finished handling ${room.roomId} with cause ${cause} (changed=${changed})`); + } return changed; } } diff --git a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts index 3acd9f924e..88789d3a50 100644 --- a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts @@ -87,9 +87,6 @@ export class ImportanceAlgorithm extends OrderingAlgorithm { public constructor(tagId: TagID, initialSortingAlgorithm: SortAlgorithm) { super(tagId, initialSortingAlgorithm); - - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Constructed an ImportanceAlgorithm for ${tagId}`); } // noinspection JSMethodCanBeStatic diff --git a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts index 849c8a2877..ae1a2c98f6 100644 --- a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts @@ -28,9 +28,6 @@ export class NaturalAlgorithm extends OrderingAlgorithm { public constructor(tagId: TagID, initialSortingAlgorithm: SortAlgorithm) { super(tagId, initialSortingAlgorithm); - - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log(`[RoomListDebug] Constructed a NaturalAlgorithm for ${tagId}`); } public async setRooms(rooms: Room[]): Promise { diff --git a/src/stores/room-list/filters/CommunityFilterCondition.ts b/src/stores/room-list/filters/CommunityFilterCondition.ts index 9f7d8daaa3..45e65fb4f4 100644 --- a/src/stores/room-list/filters/CommunityFilterCondition.ts +++ b/src/stores/room-list/filters/CommunityFilterCondition.ts @@ -52,8 +52,6 @@ export class CommunityFilterCondition extends EventEmitter implements IFilterCon const beforeRoomIds = this.roomIds; this.roomIds = (await GroupStore.getGroupRooms(this.community.groupId)).map(r => r.roomId); if (arrayHasDiff(beforeRoomIds, this.roomIds)) { - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log("Updating filter for group: ", this.community.groupId); this.emit(FILTER_CHANGED); } }; diff --git a/src/stores/room-list/filters/NameFilterCondition.ts b/src/stores/room-list/filters/NameFilterCondition.ts index 12f147990d..6014a122f8 100644 --- a/src/stores/room-list/filters/NameFilterCondition.ts +++ b/src/stores/room-list/filters/NameFilterCondition.ts @@ -41,8 +41,6 @@ export class NameFilterCondition extends EventEmitter implements IFilterConditio public set search(val: string) { this._search = val; - // TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035 - console.log("Updating filter for room name search:", this._search); this.emit(FILTER_CHANGED); } diff --git a/src/utils/MarkedExecution.ts b/src/utils/MarkedExecution.ts new file mode 100644 index 0000000000..de6cf05953 --- /dev/null +++ b/src/utils/MarkedExecution.ts @@ -0,0 +1,67 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * A utility to ensure that a function is only called once triggered with + * a mark applied. Multiple marks can be applied to the function, however + * the function will only be called once upon trigger(). + * + * The function starts unmarked. + */ +export class MarkedExecution { + private marked = false; + + /** + * Creates a MarkedExecution for the provided function. + * @param fn The function to be called upon trigger if marked. + */ + constructor(private fn: () => void) { + } + + /** + * Resets the mark without calling the function. + */ + public reset() { + this.marked = false; + } + + /** + * Marks the function to be called upon trigger(). + */ + public mark() { + this.marked = true; + } + + /** + * If marked, the function will be called, otherwise this does nothing. + */ + public trigger() { + if (!this.marked) return; + this.reset(); // reset first just in case the fn() causes a trigger() + this.fn(); + } + + /** + * Triggers the function if a mark() call would mark it. If the function + * has already been marked this will do nothing. + */ + public triggerIfWillMark() { + if (!this.marked) { + this.mark(); + this.trigger(); + } + } +}