Merge branch 'develop' into joriks/delabs-font-scaling

pull/21833/head
Travis Ralston 2020-07-14 14:48:21 -06:00
commit 76fbb7c1b0
19 changed files with 93 additions and 299 deletions

View File

@ -47,12 +47,6 @@ limitations under the License.
padding-bottom: 8px; padding-bottom: 8px;
height: 24px; height: 24px;
// Hide the header container if the contained element is stickied.
// We don't use display:none as that causes the header to go away too.
&.mx_RoomSublist2_headerContainer_hasSticky {
height: 0;
}
.mx_RoomSublist2_stickable { .mx_RoomSublist2_stickable {
flex: 1; flex: 1;
max-width: 100%; max-width: 100%;
@ -180,6 +174,15 @@ limitations under the License.
} }
} }
// In the general case, we leave height of headers alone even if sticky, so
// that the sublists below them do not jump. However, that leaves a gap
// when scrolled to the top above the first sublist (whose header can only
// ever stick to top), so we force height to 0 for only that first header.
// See also https://github.com/vector-im/riot-web/issues/14429.
&:first-child .mx_RoomSublist2_headerContainer {
height: 0;
}
.mx_RoomSublist2_resizeBox { .mx_RoomSublist2_resizeBox {
position: relative; position: relative;
@ -196,6 +199,8 @@ limitations under the License.
// as the box model should be top aligned. Happens in both FF and Chromium // as the box model should be top aligned. Happens in both FF and Chromium
display: flex; display: flex;
flex-direction: column; flex-direction: column;
mask-image: linear-gradient(0deg, transparent, black 3px);
} }
.mx_RoomSublist2_resizerHandles_showNButton { .mx_RoomSublist2_resizerHandles_showNButton {

View File

@ -37,9 +37,6 @@ declare global {
mx_RoomListStore2: RoomListStore2; mx_RoomListStore2: RoomListStore2;
mx_RoomListLayoutStore: RoomListLayoutStore; mx_RoomListLayoutStore: RoomListLayoutStore;
mxPlatformPeg: PlatformPeg; 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 // workaround for https://github.com/microsoft/TypeScript/issues/30933

View File

@ -35,17 +35,8 @@ import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomLi
import {Key} from "../../Keyboard"; import {Key} from "../../Keyboard";
import IndicatorScrollbar from "../structures/IndicatorScrollbar"; import IndicatorScrollbar from "../structures/IndicatorScrollbar";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 // TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
/*******************************************************************
* CAUTION *
*******************************************************************
* This is a work in progress implementation and isn't complete or *
* even useful as a component. Please avoid using it until this *
* warning disappears. *
*******************************************************************/
interface IProps { interface IProps {
isMinimized: boolean; isMinimized: boolean;
resizeNotifier: ResizeNotifier; resizeNotifier: ResizeNotifier;
@ -111,6 +102,10 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
const newVal = BreadcrumbsStore.instance.visible; const newVal = BreadcrumbsStore.instance.visible;
if (newVal !== this.state.showBreadcrumbs) { if (newVal !== this.state.showBreadcrumbs) {
this.setState({showBreadcrumbs: newVal}); this.setState({showBreadcrumbs: newVal});
// Update the sticky headers too as the breadcrumbs will be popping in or out.
if (!this.listContainerRef.current) return; // ignore: no headers to sticky
this.handleStickyHeaders(this.listContainerRef.current);
} }
}; };
@ -170,7 +165,6 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
// layout updates. // layout updates.
for (const header of targetStyles.keys()) { for (const header of targetStyles.keys()) {
const style = targetStyles.get(header); const style = targetStyles.get(header);
const headerContainer = header.parentElement; // .mx_RoomSublist2_headerContainer
if (style.makeInvisible) { if (style.makeInvisible) {
// we will have already removed the 'display: none', so add it back. // we will have already removed the 'display: none', so add it back.
@ -187,19 +181,29 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
if (header.style.top !== newTop) { if (header.style.top !== newTop) {
header.style.top = newTop; header.style.top = newTop;
} }
} else if (style.stickyBottom) { } else {
if (header.classList.contains("mx_RoomSublist2_headerContainer_stickyTop")) {
header.classList.remove("mx_RoomSublist2_headerContainer_stickyTop");
}
if (header.style.top) {
header.style.removeProperty('top');
}
}
if (style.stickyBottom) {
if (!header.classList.contains("mx_RoomSublist2_headerContainer_stickyBottom")) { if (!header.classList.contains("mx_RoomSublist2_headerContainer_stickyBottom")) {
header.classList.add("mx_RoomSublist2_headerContainer_stickyBottom"); header.classList.add("mx_RoomSublist2_headerContainer_stickyBottom");
} }
} else {
if (header.classList.contains("mx_RoomSublist2_headerContainer_stickyBottom")) {
header.classList.remove("mx_RoomSublist2_headerContainer_stickyBottom");
}
} }
if (style.stickyTop || style.stickyBottom) { if (style.stickyTop || style.stickyBottom) {
if (!header.classList.contains("mx_RoomSublist2_headerContainer_sticky")) { if (!header.classList.contains("mx_RoomSublist2_headerContainer_sticky")) {
header.classList.add("mx_RoomSublist2_headerContainer_sticky"); header.classList.add("mx_RoomSublist2_headerContainer_sticky");
} }
if (!headerContainer.classList.contains("mx_RoomSublist2_headerContainer_hasSticky")) {
headerContainer.classList.add("mx_RoomSublist2_headerContainer_hasSticky");
}
const newWidth = `${headerStickyWidth}px`; const newWidth = `${headerStickyWidth}px`;
if (header.style.width !== newWidth) { if (header.style.width !== newWidth) {
@ -209,21 +213,9 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
if (header.classList.contains("mx_RoomSublist2_headerContainer_sticky")) { if (header.classList.contains("mx_RoomSublist2_headerContainer_sticky")) {
header.classList.remove("mx_RoomSublist2_headerContainer_sticky"); header.classList.remove("mx_RoomSublist2_headerContainer_sticky");
} }
if (header.classList.contains("mx_RoomSublist2_headerContainer_stickyTop")) {
header.classList.remove("mx_RoomSublist2_headerContainer_stickyTop");
}
if (header.classList.contains("mx_RoomSublist2_headerContainer_stickyBottom")) {
header.classList.remove("mx_RoomSublist2_headerContainer_stickyBottom");
}
if (headerContainer.classList.contains("mx_RoomSublist2_headerContainer_hasSticky")) {
headerContainer.classList.remove("mx_RoomSublist2_headerContainer_hasSticky");
}
if (header.style.width) { if (header.style.width) {
header.style.removeProperty('width'); header.style.removeProperty('width');
} }
if (header.style.top) {
header.style.removeProperty('top');
}
} }
} }
@ -242,7 +234,6 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
} }
} }
// TODO: Improve header reliability: https://github.com/vector-im/riot-web/issues/14232
private onScroll = (ev: React.MouseEvent<HTMLDivElement>) => { private onScroll = (ev: React.MouseEvent<HTMLDivElement>) => {
const list = ev.target as HTMLDivElement; const list = ev.target as HTMLDivElement;
this.handleStickyHeaders(list); this.handleStickyHeaders(list);
@ -383,8 +374,6 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
onResize={this.onResize} onResize={this.onResize}
/>; />;
// TODO: Conference handling / calls: https://github.com/vector-im/riot-web/issues/14177
const containerClasses = classNames({ const containerClasses = classNames({
"mx_LeftPanel2": true, "mx_LeftPanel2": true,
"mx_LeftPanel2_hasTagPanel": !!tagPanel, "mx_LeftPanel2_hasTagPanel": !!tagPanel,

View File

@ -25,16 +25,6 @@ import { Key } from "../../Keyboard";
import AccessibleButton from "../views/elements/AccessibleButton"; import AccessibleButton from "../views/elements/AccessibleButton";
import { Action } from "../../dispatcher/actions"; import { Action } from "../../dispatcher/actions";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367
/*******************************************************************
* CAUTION *
*******************************************************************
* This is a work in progress implementation and isn't complete or *
* even useful as a component. Please avoid using it until this *
* warning disappears. *
*******************************************************************/
interface IProps { interface IProps {
onQueryUpdate: (newQuery: string) => void; onQueryUpdate: (newQuery: string) => void;
isMinimized: boolean; isMinimized: boolean;

View File

@ -170,6 +170,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
ev.stopPropagation(); ev.stopPropagation();
// TODO: Archived room view: https://github.com/vector-im/riot-web/issues/14038 // TODO: Archived room view: https://github.com/vector-im/riot-web/issues/14038
// Note: You'll need to uncomment the button too.
console.log("TODO: Show archived rooms"); console.log("TODO: Show archived rooms");
}; };

View File

@ -18,8 +18,6 @@ import React from "react";
import classNames from "classnames"; import classNames from "classnames";
import { formatMinimalBadgeCount } from "../../../utils/FormattingUtils"; import { formatMinimalBadgeCount } from "../../../utils/FormattingUtils";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { readReceiptChangeIsFor } from "../../../utils/read-receipts";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import { XOR } from "../../../@types/common"; import { XOR } from "../../../@types/common";
import { NOTIFICATION_STATE_UPDATE, NotificationState } from "../../../stores/notifications/NotificationState"; import { NOTIFICATION_STATE_UPDATE, NotificationState } from "../../../stores/notifications/NotificationState";

View File

@ -27,17 +27,8 @@ import RoomListStore from "../../../stores/room-list/RoomListStore2";
import { DefaultTagID } from "../../../stores/room-list/models"; import { DefaultTagID } from "../../../stores/room-list/models";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 // TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
/*******************************************************************
* CAUTION *
*******************************************************************
* This is a work in progress implementation and isn't complete or *
* even useful as a component. Please avoid using it until this *
* warning disappears. *
*******************************************************************/
interface IProps { interface IProps {
} }

View File

@ -41,17 +41,8 @@ import { Action } from "../../../dispatcher/actions";
import { ViewRoomDeltaPayload } from "../../../dispatcher/payloads/ViewRoomDeltaPayload"; import { ViewRoomDeltaPayload } from "../../../dispatcher/payloads/ViewRoomDeltaPayload";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 // TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
/*******************************************************************
* CAUTION *
*******************************************************************
* This is a work in progress implementation and isn't complete or *
* even useful as a component. Please avoid using it until this *
* warning disappears. *
*******************************************************************/
interface IProps { interface IProps {
onKeyDown: (ev: React.KeyboardEvent) => void; onKeyDown: (ev: React.KeyboardEvent) => void;
onFocus: (ev: React.FocusEvent) => void; onFocus: (ev: React.FocusEvent) => void;
@ -218,19 +209,14 @@ export default class RoomList2 extends React.Component<IProps, IState> {
}; };
private updateLists = () => { private updateLists = () => {
const newLists = RoomListStore.instance.orderedLists; this.setState({sublists: RoomListStore.instance.orderedLists}, () => {
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(); this.props.onResize();
}); });
}; };
private renderCommunityInvites(): React.ReactElement[] { private renderCommunityInvites(): React.ReactElement[] {
// TODO: Put community invites in a more sensible place (not in the room list) // TODO: Put community invites in a more sensible place (not in the room list)
// See https://github.com/vector-im/riot-web/issues/14456
return MatrixClientPeg.get().getGroups().filter(g => { return MatrixClientPeg.get().getGroups().filter(g => {
if (g.myMembership !== 'invite') return false; if (g.myMembership !== 'invite') return false;
return !this.searchFilter || this.searchFilter.matches(g.name || ""); return !this.searchFilter || this.searchFilter.matches(g.name || "");

View File

@ -17,7 +17,7 @@ limitations under the License.
*/ */
import * as React from "react"; import * as React from "react";
import {createRef, UIEventHandler} from "react"; import {createRef} from "react";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import classNames from 'classnames'; import classNames from 'classnames';
import { RovingAccessibleButton, RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import { RovingAccessibleButton, RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
@ -48,17 +48,8 @@ import { polyfillTouchEvent } from "../../../@types/polyfill";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore"; import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 // TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
/*******************************************************************
* CAUTION *
*******************************************************************
* This is a work in progress implementation and isn't complete or *
* even useful as a component. Please avoid using it until this *
* warning disappears. *
*******************************************************************/
const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS
const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS
export const HEADER_HEIGHT = 32; // As defined by CSS export const HEADER_HEIGHT = 32; // As defined by CSS
@ -137,9 +128,10 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
let padding = RESIZE_HANDLE_HEIGHT; let padding = RESIZE_HANDLE_HEIGHT;
// this is used for calculating the max height of the whole container, // 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 // 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, // when fully expanded. We cannot check against the layout's defaultVisible tile count
// but in this case it will take the space of a tile and we don't need to reserve space for it. // because there are conditions in which we need to know that the 'show more' button
if (this.numTiles > this.layout.defaultVisibleTiles) { // is present while well under the default tile limit.
if (this.numTiles > this.numVisibleTiles) {
padding += SHOW_N_BUTTON_HEIGHT; padding += SHOW_N_BUTTON_HEIGHT;
} }
return padding; return padding;
@ -236,10 +228,13 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
}; };
private onShowAllClick = () => { private onShowAllClick = () => {
// read number of visible tiles before we mutate it
const numVisibleTiles = this.numVisibleTiles;
const newHeight = this.layout.tilesToPixelsWithPadding(this.numTiles, this.padding); const newHeight = this.layout.tilesToPixelsWithPadding(this.numTiles, this.padding);
this.applyHeightChange(newHeight); this.applyHeightChange(newHeight);
this.setState({height: newHeight}, () => { this.setState({height: newHeight}, () => {
this.focusRoomTile(this.numTiles - 1); // focus the top-most new room
this.focusRoomTile(numVisibleTiles);
}); });
}; };
@ -606,8 +601,6 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
} }
public render(): React.ReactElement { public render(): React.ReactElement {
// TODO: Error boundary: https://github.com/vector-im/riot-web/issues/14185
const visibleTiles = this.renderVisibleTiles(); const visibleTiles = this.renderVisibleTiles();
const classes = classNames({ const classes = classNames({
'mx_RoomSublist2': true, 'mx_RoomSublist2': true,
@ -623,11 +616,15 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
const showMoreAtMinHeight = minTiles < this.numTiles; const showMoreAtMinHeight = minTiles < this.numTiles;
const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0); const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0);
const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding); const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding);
const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding); let maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding);
const showMoreBtnClasses = classNames({ const showMoreBtnClasses = classNames({
'mx_RoomSublist2_showNButton': true, 'mx_RoomSublist2_showNButton': true,
}); });
if (this.numTiles > this.layout.defaultVisibleTiles) {
maxTilesPx += SHOW_N_BUTTON_HEIGHT;
}
// If we're hiding rooms, show a 'show more' button to the user. This button // If we're hiding rooms, show a 'show more' button to the user. This button
// floats above the resize handle, if we have one present. If the user has all // floats above the resize handle, if we have one present. If the user has all
// tiles visible, it becomes 'show less'. // tiles visible, it becomes 'show less'.

View File

@ -55,17 +55,8 @@ import {ActionPayload} from "../../../dispatcher/payloads";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import { NotificationState } from "../../../stores/notifications/NotificationState"; import { NotificationState } from "../../../stores/notifications/NotificationState";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 // TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
/*******************************************************************
* CAUTION *
*******************************************************************
* This is a work in progress implementation and isn't complete or *
* even useful as a component. Please avoid using it until this *
* warning disappears. *
*******************************************************************/
interface IProps { interface IProps {
room: Room; room: Room;
showMessagePreview: boolean; showMessagePreview: boolean;
@ -124,7 +115,6 @@ const NotifOption: React.FC<INotifOptionProps> = ({active, onClick, iconClassNam
export default class RoomTile2 extends React.Component<IProps, IState> { export default class RoomTile2 extends React.Component<IProps, IState> {
private dispatcherRef: string; private dispatcherRef: string;
private roomTileRef = createRef<HTMLDivElement>(); private roomTileRef = createRef<HTMLDivElement>();
// TODO: a11y: https://github.com/vector-im/riot-web/issues/14180
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -310,7 +300,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
await setRoomNotifsState(this.props.room.roomId, newState); await setRoomNotifsState(this.props.room.roomId, newState);
} catch (error) { } catch (error) {
// TODO: some form of error notification to the user to inform them that their state change failed. // TODO: some form of error notification to the user to inform them that their state change failed.
// https://github.com/vector-im/riot-web/issues/14281 // See https://github.com/vector-im/riot-web/issues/14281
console.error(error); console.error(error);
} }
@ -398,8 +388,6 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
private renderGeneralMenu(): React.ReactElement { private renderGeneralMenu(): React.ReactElement {
if (!this.showContextMenu) return null; // no menu to show if (!this.showContextMenu) return null; // no menu to show
// TODO: We could do with a proper invite context menu, unlike what showContextMenu suggests
const roomTags = RoomListStore.instance.getTagsForRoom(this.props.room); const roomTags = RoomListStore.instance.getTagsForRoom(this.props.room);
const isFavorite = roomTags.includes(DefaultTagID.Favourite); const isFavorite = roomTags.includes(DefaultTagID.Favourite);
@ -465,8 +453,6 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
} }
public render(): React.ReactElement { public render(): React.ReactElement {
// TODO: Invites: https://github.com/vector-im/riot-web/issues/14198
const classes = classNames({ const classes = classNames({
'mx_RoomTile2': true, 'mx_RoomTile2': true,
'mx_RoomTile2_selected': this.state.selected, 'mx_RoomTile2_selected': this.state.selected,
@ -495,7 +481,6 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
); );
} }
// TODO: the original RoomTile uses state for the room name. Do we need to?
let name = this.props.room.name; let name = this.props.room.name;
if (typeof name !== 'string') name = ''; if (typeof name !== 'string') name = '';
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon

View File

@ -34,6 +34,7 @@ interface IState {
hover: boolean; hover: boolean;
} }
// TODO: Remove with community invites in the room list: https://github.com/vector-im/riot-web/issues/14456
export default class TemporaryTile extends React.Component<IProps, IState> { export default class TemporaryTile extends React.Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);

View File

@ -21,6 +21,7 @@ import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
import defaultDispatcher from "../dispatcher/dispatcher"; import defaultDispatcher from "../dispatcher/dispatcher";
import { arrayHasDiff } from "../utils/arrays"; import { arrayHasDiff } from "../utils/arrays";
import { RoomListStoreTempProxy } from "./room-list/RoomListStoreTempProxy"; import { RoomListStoreTempProxy } from "./room-list/RoomListStoreTempProxy";
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
const MAX_ROOMS = 20; // arbitrary const MAX_ROOMS = 20; // arbitrary
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up
@ -51,7 +52,11 @@ export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
} }
public get visible(): boolean { public get visible(): boolean {
return this.state.enabled && this.matrixClient.getVisibleRooms().length >= 20; return this.state.enabled && this.meetsRoomRequirement;
}
private get meetsRoomRequirement(): boolean {
return this.matrixClient.getVisibleRooms().length >= 20;
} }
protected async onAction(payload: ActionPayload) { protected async onAction(payload: ActionPayload) {
@ -99,8 +104,9 @@ export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
} }
private onMyMembership = async (room: Room) => { private onMyMembership = async (room: Room) => {
// We turn on breadcrumbs by default once the user has at least 1 room to show. // Only turn on breadcrumbs is the user hasn't explicitly turned it off again.
if (!this.state.enabled) { const settingValueRaw = SettingsStore.getValue("breadcrumbs", null, /*excludeDefault=*/true);
if (this.meetsRoomRequirement && isNullOrUndefined(settingValueRaw)) {
await SettingsStore.setValue("breadcrumbs", null, SettingLevel.ACCOUNT, true); await SettingsStore.setValue("breadcrumbs", null, SettingLevel.ACCOUNT, true);
} }
}; };

View File

@ -17,7 +17,7 @@ limitations under the License.
import { NotificationColor } from "./NotificationColor"; import { NotificationColor } from "./NotificationColor";
import { IDestroyable } from "../../utils/IDestroyable"; import { IDestroyable } from "../../utils/IDestroyable";
import { MatrixClientPeg } from "../../MatrixClientPeg"; import { MatrixClientPeg } from "../../MatrixClientPeg";
import { EffectiveMembership, getEffectiveMembership } from "../room-list/membership"; import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
import { readReceiptChangeIsFor } from "../../utils/read-receipts"; import { readReceiptChangeIsFor } from "../../utils/read-receipts";
import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";

View File

@ -19,7 +19,6 @@ import { MatrixClient } from "matrix-js-sdk/src/client";
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import { DefaultTagID, OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models"; import { DefaultTagID, OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models";
import TagOrderStore from "../TagOrderStore"; import TagOrderStore from "../TagOrderStore";
import { AsyncStore } from "../AsyncStore";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { IListOrderingMap, ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; import { IListOrderingMap, ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models";
import { ActionPayload } from "../../dispatcher/payloads"; import { ActionPayload } from "../../dispatcher/payloads";
@ -29,10 +28,11 @@ import { FILTER_CHANGED, IFilterCondition } from "./filters/IFilterCondition";
import { TagWatcher } from "./TagWatcher"; import { TagWatcher } from "./TagWatcher";
import RoomViewStore from "../RoomViewStore"; import RoomViewStore from "../RoomViewStore";
import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm"; import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm";
import { EffectiveMembership, getEffectiveMembership } from "./membership"; import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
import RoomListLayoutStore from "./RoomListLayoutStore"; import RoomListLayoutStore from "./RoomListLayoutStore";
import { MarkedExecution } from "../../utils/MarkedExecution"; import { MarkedExecution } from "../../utils/MarkedExecution";
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
interface IState { interface IState {
tagsEnabled?: boolean; tagsEnabled?: boolean;
@ -44,14 +44,13 @@ interface IState {
*/ */
export const LISTS_UPDATE_EVENT = "lists_update"; export const LISTS_UPDATE_EVENT = "lists_update";
export class RoomListStore2 extends AsyncStore<ActionPayload> { export class RoomListStore2 extends AsyncStoreWithClient<ActionPayload> {
/** /**
* Set to true if you're running tests on the store. Should not be touched in * Set to true if you're running tests on the store. Should not be touched in
* any other environment. * any other environment.
*/ */
public static TEST_MODE = false; public static TEST_MODE = false;
private _matrixClient: MatrixClient;
private initialListsGenerated = false; private initialListsGenerated = false;
private enabled = false; private enabled = false;
private algorithm = new Algorithm(); private algorithm = new Algorithm();
@ -79,7 +78,7 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
} }
public get matrixClient(): MatrixClient { public get matrixClient(): MatrixClient {
return this._matrixClient; return super.matrixClient;
} }
// Intended for test usage // Intended for test usage
@ -88,23 +87,28 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
this.tagWatcher = new TagWatcher(this); this.tagWatcher = new TagWatcher(this);
this.filterConditions = []; this.filterConditions = [];
this.initialListsGenerated = false; this.initialListsGenerated = false;
this._matrixClient = null;
this.algorithm.off(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); this.algorithm.off(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated);
this.algorithm.off(FILTER_CHANGED, this.onAlgorithmListUpdated); this.algorithm.off(FILTER_CHANGED, this.onAlgorithmListUpdated);
this.algorithm = new Algorithm(); this.algorithm = new Algorithm();
this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated);
this.algorithm.on(FILTER_CHANGED, 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 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 // TODO: Remove with https://github.com/vector-im/riot-web/issues/14367
this.checkEnabled(); this.checkEnabled();
if (!this.enabled) return; 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.
// 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"); console.log("Regenerating room lists: Startup");
@ -150,10 +154,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
console.warn(`${activeRoomId} is current in RVS but missing from client - clearing sticky room`); console.warn(`${activeRoomId} is current in RVS but missing from client - clearing sticky room`);
await this.algorithm.setStickyRoom(null); await this.algorithm.setStickyRoom(null);
} else if (activeRoom !== this.algorithm.stickyRoom) { } else if (activeRoom !== this.algorithm.stickyRoom) {
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); await this.algorithm.setStickyRoom(activeRoom);
} }
} }
@ -161,7 +161,15 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
if (trigger) this.updateFn.trigger(); if (trigger) this.updateFn.trigger();
} }
protected async onDispatch(payload: ActionPayload) { protected async onReady(): Promise<any> {
await this.makeReady();
}
protected async onNotReady(): Promise<any> {
await this.resetStore();
}
protected async onAction(payload: ActionPayload) {
// When we're running tests we can't reliably use setImmediate out of timing concerns. // When we're running tests we can't reliably use setImmediate out of timing concerns.
// As such, we use a more synchronous model. // As such, we use a more synchronous model.
if (RoomListStore2.TEST_MODE) { if (RoomListStore2.TEST_MODE) {
@ -175,29 +183,10 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
} }
protected async onDispatchAsync(payload: ActionPayload) { 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 // TODO: Remove this once the RoomListStore becomes default
if (!this.enabled) return; if (!this.enabled) return;
if (payload.action === 'on_client_not_viable' || payload.action === 'on_logged_out') { // Everything here requires a MatrixClient or some sort of logical readiness.
// 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.
const logicallyReady = this.matrixClient && this.initialListsGenerated; const logicallyReady = this.matrixClient && this.initialListsGenerated;
if (!logicallyReady) return; if (!logicallyReady) return;
@ -225,20 +214,12 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
console.warn(`Own read receipt was in unknown room ${room.roomId}`); console.warn(`Own read receipt was in unknown room ${room.roomId}`);
return; return;
} }
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); await this.handleRoomUpdate(room, RoomUpdateCause.ReadReceipt);
this.updateFn.trigger(); this.updateFn.trigger();
return; return;
} }
} else if (payload.action === 'MatrixActions.Room.tags') { } else if (payload.action === 'MatrixActions.Room.tags') {
const roomPayload = (<any>payload); // TODO: Type out the dispatcher types const roomPayload = (<any>payload); // TODO: Type out the dispatcher types
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); await this.handleRoomUpdate(roomPayload.room, RoomUpdateCause.PossibleTagChange);
this.updateFn.trigger(); this.updateFn.trigger();
} else if (payload.action === 'MatrixActions.Room.timeline') { } else if (payload.action === 'MatrixActions.Room.timeline') {
@ -250,16 +231,7 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
const roomId = eventPayload.event.getRoomId(); const roomId = eventPayload.event.getRoomId();
const room = this.matrixClient.getRoom(roomId); const room = this.matrixClient.getRoom(roomId);
const tryUpdate = async (updatedRoom: Room) => { const tryUpdate = async (updatedRoom: Room) => {
if (!window.mx_QuietRoomListLogging) {
// 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 (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']); const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()['replacement_room']);
if (newRoom) { if (newRoom) {
// If we have the new room, then the new room check will have seen the predecessor // If we have the new room, then the new room check will have seen the predecessor
@ -289,18 +261,10 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
console.warn(`Event ${eventPayload.event.getId()} was decrypted in an unknown room ${roomId}`); console.warn(`Event ${eventPayload.event.getId()} was decrypted in an unknown room ${roomId}`);
return; return;
} }
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); await this.handleRoomUpdate(room, RoomUpdateCause.Timeline);
this.updateFn.trigger(); this.updateFn.trigger();
} else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') { } else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') {
const eventPayload = (<any>payload); // TODO: Type out the dispatcher types const eventPayload = (<any>payload); // TODO: Type out the dispatcher types
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(); const dmMap = eventPayload.event.getContent();
for (const userId of Object.keys(dmMap)) { for (const userId of Object.keys(dmMap)) {
const roomIds = dmMap[userId]; const roomIds = dmMap[userId];
@ -324,54 +288,29 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
const oldMembership = getEffectiveMembership(membershipPayload.oldMembership); const oldMembership = getEffectiveMembership(membershipPayload.oldMembership);
const newMembership = getEffectiveMembership(membershipPayload.membership); const newMembership = getEffectiveMembership(membershipPayload.membership);
if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) { if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) {
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 // If we're joining an upgraded room, we'll want to make sure we don't proliferate
// the dead room in the list. // the dead room in the list.
const createEvent = membershipPayload.room.currentState.getStateEvents("m.room.create", ""); const createEvent = membershipPayload.room.currentState.getStateEvents("m.room.create", "");
if (createEvent && createEvent.getContent()['predecessor']) { if (createEvent && createEvent.getContent()['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']); const prevRoom = this.matrixClient.getRoom(createEvent.getContent()['predecessor']['room_id']);
if (prevRoom) { if (prevRoom) {
const isSticky = this.algorithm.stickyRoom === prevRoom; const isSticky = this.algorithm.stickyRoom === prevRoom;
if (isSticky) { if (isSticky) {
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); await this.algorithm.setStickyRoom(null);
} }
// Note: we hit the algorithm instead of our handleRoomUpdate() function to // Note: we hit the algorithm instead of our handleRoomUpdate() function to
// avoid redundant updates. // avoid redundant updates.
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); await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved);
} }
} }
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); await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
this.updateFn.trigger(); this.updateFn.trigger();
return; return;
} }
if (oldMembership !== EffectiveMembership.Invite && newMembership === EffectiveMembership.Invite) { if (oldMembership !== EffectiveMembership.Invite && newMembership === EffectiveMembership.Invite) {
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); await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
this.updateFn.trigger(); this.updateFn.trigger();
return; return;
@ -379,10 +318,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
// If it's not a join, it's transitioning into a different list (possibly historical) // If it's not a join, it's transitioning into a different list (possibly historical)
if (oldMembership !== newMembership) { if (oldMembership !== newMembership) {
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); await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange);
this.updateFn.trigger(); this.updateFn.trigger();
return; return;
@ -393,10 +328,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<any> { private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<any> {
const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause); const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause);
if (shouldUpdate) { if (shouldUpdate) {
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(); this.updateFn.mark();
} }
} }
@ -516,10 +447,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
} }
private onAlgorithmListUpdated = () => { private onAlgorithmListUpdated = () => {
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(); this.updateFn.mark();
}; };
@ -566,10 +493,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
} }
public addFilter(filter: IFilterCondition): void { public addFilter(filter: IFilterCondition): void {
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); this.filterConditions.push(filter);
if (this.algorithm) { if (this.algorithm) {
this.algorithm.addFilterCondition(filter); this.algorithm.addFilterCondition(filter);
@ -578,10 +501,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
} }
public removeFilter(filter: IFilterCondition): void { public removeFilter(filter: IFilterCondition): void {
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); const idx = this.filterConditions.indexOf(filter);
if (idx >= 0) { if (idx >= 0) {
this.filterConditions.splice(idx, 1); this.filterConditions.splice(idx, 1);

View File

@ -30,12 +30,10 @@ import {
SortAlgorithm SortAlgorithm
} from "./models"; } from "./models";
import { FILTER_CHANGED, FilterPriority, IFilterCondition } from "../filters/IFilterCondition"; import { FILTER_CHANGED, FilterPriority, IFilterCondition } from "../filters/IFilterCondition";
import { EffectiveMembership, getEffectiveMembership, splitRoomsByMembership } from "../membership"; import { EffectiveMembership, getEffectiveMembership, splitRoomsByMembership } from "../../../utils/membership";
import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm"; import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm";
import { getListAlgorithmInstance } from "./list-ordering"; import { getListAlgorithmInstance } from "./list-ordering";
// TODO: Add locking support to avoid concurrent writes? https://github.com/vector-im/riot-web/issues/14235
/** /**
* Fired when the Algorithm has determined a list has been updated. * Fired when the Algorithm has determined a list has been updated.
*/ */
@ -321,11 +319,6 @@ export class Algorithm extends EventEmitter {
} }
} }
newMap[tagId] = allowedRoomsInThisTag; newMap[tagId] = allowedRoomsInThisTag;
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; }, <Room[]>[]); const allowedRooms = Object.values(newMap).reduce((rv, v) => { rv.push(...v); return rv; }, <Room[]>[]);
@ -337,10 +330,6 @@ export class Algorithm extends EventEmitter {
protected recalculateFilteredRoomsForTag(tagId: TagID): void { protected recalculateFilteredRoomsForTag(tagId: TagID): void {
if (!this.hasFilters) return; // don't bother doing work if there's nothing to do if (!this.hasFilters) return; // don't bother doing work if there's nothing to do
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]; delete this.filteredRooms[tagId];
const rooms = this.cachedRooms[tagId].map(r => r); // cheap clone const rooms = this.cachedRooms[tagId].map(r => r); // cheap clone
this.tryInsertStickyRoomToFilterSet(rooms, tagId); this.tryInsertStickyRoomToFilterSet(rooms, tagId);
@ -348,11 +337,6 @@ export class Algorithm extends EventEmitter {
if (filteredRooms.length > 0) { if (filteredRooms.length > 0) {
this.filteredRooms[tagId] = filteredRooms; this.filteredRooms[tagId] = filteredRooms;
} }
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) { protected tryInsertStickyRoomToFilterSet(rooms: Room[], tagId: TagID) {
@ -391,10 +375,6 @@ export class Algorithm extends EventEmitter {
} }
if (!this._cachedStickyRooms || !updatedTag) { if (!this._cachedStickyRooms || !updatedTag) {
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 = {}; const stickiedTagMap: ITagMap = {};
for (const tagId of Object.keys(this.cachedRooms)) { for (const tagId of Object.keys(this.cachedRooms)) {
stickiedTagMap[tagId] = this.cachedRooms[tagId].map(r => r); // shallow clone stickiedTagMap[tagId] = this.cachedRooms[tagId].map(r => r); // shallow clone
@ -405,10 +385,6 @@ export class Algorithm extends EventEmitter {
if (updatedTag) { if (updatedTag) {
// Update the tag indicated by the caller, if possible. This is mostly to ensure // Update the tag indicated by the caller, if possible. This is mostly to ensure
// our cache is up to date. // our cache is up to date.
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 this._cachedStickyRooms[updatedTag] = this.cachedRooms[updatedTag].map(r => r); // shallow clone
} }
@ -417,10 +393,6 @@ export class Algorithm extends EventEmitter {
// we might have updated from the cache is also our sticky room. // we might have updated from the cache is also our sticky room.
const sticky = this._stickyRoom; const sticky = this._stickyRoom;
if (!updatedTag || updatedTag === sticky.tag) { if (!updatedTag || updatedTag === 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); this._cachedStickyRooms[sticky.tag].splice(sticky.position, 0, sticky.room);
} }
@ -645,10 +617,6 @@ export class Algorithm extends EventEmitter {
* processing. * processing.
*/ */
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> { public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
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"); 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 // Note: check the isSticky against the room ID just in case the reference is wrong
@ -698,27 +666,24 @@ export class Algorithm extends EventEmitter {
} }
} }
if (cause === RoomUpdateCause.PossibleTagChange) {
let didTagChange = false; let didTagChange = false;
if (cause === RoomUpdateCause.PossibleTagChange) {
const oldTags = this.roomIdsToTags[room.roomId] || []; const oldTags = this.roomIdsToTags[room.roomId] || [];
const newTags = this.getTagsForRoom(room); const newTags = this.getTagsForRoom(room);
const diff = arrayDiff(oldTags, newTags); const diff = arrayDiff(oldTags, newTags);
if (diff.removed.length > 0 || diff.added.length > 0) { if (diff.removed.length > 0 || diff.added.length > 0) {
for (const rmTag of diff.removed) { for (const rmTag of diff.removed) {
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]; const algorithm: OrderingAlgorithm = this.algorithms[rmTag];
if (!algorithm) throw new Error(`No algorithm for ${rmTag}`); if (!algorithm) throw new Error(`No algorithm for ${rmTag}`);
await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved); await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved);
this.cachedRooms[rmTag] = algorithm.orderedRooms; 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) { for (const addTag of diff.added) {
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]; const algorithm: OrderingAlgorithm = this.algorithms[addTag];
if (!algorithm) throw new Error(`No algorithm for ${addTag}`); if (!algorithm) throw new Error(`No algorithm for ${addTag}`);
await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom); await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom);
@ -728,17 +693,9 @@ export class Algorithm extends EventEmitter {
// Update the tag map so we don't regen it in a moment // Update the tag map so we don't regen it in a moment
this.roomIdsToTags[room.roomId] = newTags; this.roomIdsToTags[room.roomId] = newTags;
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; cause = RoomUpdateCause.Timeline;
didTagChange = true; didTagChange = true;
} else { } else {
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; cause = RoomUpdateCause.Timeline;
} }
@ -764,28 +721,15 @@ export class Algorithm extends EventEmitter {
// as the sticky room relies on this. // as the sticky room relies on this.
if (cause !== RoomUpdateCause.NewRoom && cause !== RoomUpdateCause.RoomRemoved) { if (cause !== RoomUpdateCause.NewRoom && cause !== RoomUpdateCause.RoomRemoved) {
if (this.stickyRoom === room) { if (this.stickyRoom === room) {
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; return false;
} }
} }
if (!this.roomIdsToTags[room.roomId]) { if (!this.roomIdsToTags[room.roomId]) {
if (CAUSES_REQUIRING_ROOM.includes(cause)) { if (CAUSES_REQUIRING_ROOM.includes(cause)) {
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; return false;
} }
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 // Get the tags for the room and populate the cache
const roomTags = this.getTagsForRoom(room).filter(t => !isNullOrUndefined(this.cachedRooms[t])); const roomTags = this.getTagsForRoom(room).filter(t => !isNullOrUndefined(this.cachedRooms[t]));
@ -794,16 +738,6 @@ export class Algorithm extends EventEmitter {
if (!roomTags.length) throw new Error(`Tags cannot be determined for ${room.roomId}`); if (!roomTags.length) throw new Error(`Tags cannot be determined for ${room.roomId}`);
this.roomIdsToTags[room.roomId] = roomTags; this.roomIdsToTags[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);
}
}
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]; const tags = this.roomIdsToTags[room.roomId];
@ -812,7 +746,7 @@ export class Algorithm extends EventEmitter {
return false; return false;
} }
let changed = false; let changed = didTagChange;
for (const tag of tags) { for (const tag of tags) {
const algorithm: OrderingAlgorithm = this.algorithms[tag]; const algorithm: OrderingAlgorithm = this.algorithms[tag];
if (!algorithm) throw new Error(`No algorithm for ${tag}`); if (!algorithm) throw new Error(`No algorithm for ${tag}`);
@ -826,10 +760,6 @@ export class Algorithm extends EventEmitter {
changed = true; changed = true;
} }
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; return changed;
} }
} }

View File

@ -55,7 +55,7 @@ export class NaturalAlgorithm extends OrderingAlgorithm {
} }
} }
// TODO: Optimize this to avoid useless operations: https://github.com/vector-im/riot-web/issues/14035 // TODO: Optimize this to avoid useless operations: https://github.com/vector-im/riot-web/issues/14457
// For example, we can skip updates to alphabetic (sometimes) and manually ordered tags // For example, we can skip updates to alphabetic (sometimes) and manually ordered tags
this.cachedOrderedRooms = await sortRoomsWithAlgorithm(this.cachedOrderedRooms, this.tagId, this.sortingAlgorithm); this.cachedOrderedRooms = await sortRoomsWithAlgorithm(this.cachedOrderedRooms, this.tagId, this.sortingAlgorithm);

View File

@ -17,8 +17,6 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { TagID } from "../../models"; import { TagID } from "../../models";
import { IAlgorithm } from "./IAlgorithm"; import { IAlgorithm } from "./IAlgorithm";
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import * as Unread from "../../../../Unread";
/** /**
* Sorts rooms according to the browser's determination of alphabetic. * Sorts rooms according to the browser's determination of alphabetic.

View File

@ -19,7 +19,7 @@ import { TagID } from "../../models";
import { IAlgorithm } from "./IAlgorithm"; import { IAlgorithm } from "./IAlgorithm";
import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import * as Unread from "../../../../Unread"; import * as Unread from "../../../../Unread";
import { EffectiveMembership, getEffectiveMembership } from "../../membership"; import { EffectiveMembership, getEffectiveMembership } from "../../../../utils/membership";
/** /**
* Sorts rooms according to the last event's timestamp in each room that seems * Sorts rooms according to the last event's timestamp in each room that seems
@ -33,12 +33,13 @@ export class RecentAlgorithm implements IAlgorithm {
// of the rooms to each other. // of the rooms to each other.
// TODO: We could probably improve the sorting algorithm here by finding changes. // TODO: We could probably improve the sorting algorithm here by finding changes.
// See https://github.com/vector-im/riot-web/issues/14035 // See https://github.com/vector-im/riot-web/issues/14459
// For example, if we spent a little bit of time to determine which elements have // For example, if we spent a little bit of time to determine which elements have
// actually changed (probably needs to be done higher up?) then we could do an // actually changed (probably needs to be done higher up?) then we could do an
// insertion sort or similar on the limited set of changes. // insertion sort or similar on the limited set of changes.
// TODO: Don't assume we're using the same client as the peg // TODO: Don't assume we're using the same client as the peg
// See https://github.com/vector-im/riot-web/issues/14458
let myUserId = ''; let myUserId = '';
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
myUserId = MatrixClientPeg.get().getUserId(); myUserId = MatrixClientPeg.get().getUserId();