Merge branch 'element' of https://gitlab.matrix.org/new-vector/element/element-web/matrix-react-sdk into element
						commit
						7880c10b21
					
				| 
						 | 
				
			
			@ -24,7 +24,7 @@ limitations under the License.
 | 
			
		|||
        right: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .mx_NotificationBadge {
 | 
			
		||||
    .mx_NotificationBadge, .mx_RoomTile2_badgeContainer {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,7 +89,6 @@ limitations under the License.
 | 
			
		|||
        height: 16px;
 | 
			
		||||
        // don't set width so that it takes no space when there is no badge to show
 | 
			
		||||
        margin: auto 0; // vertically align
 | 
			
		||||
        position: relative; // fixes badge alignment in some scenarios
 | 
			
		||||
 | 
			
		||||
        // Create a flexbox to make aligning dot badges easier
 | 
			
		||||
        display: flex;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,7 +105,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
 | 
			
		|||
        ev.target.select();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onBlur = () => {
 | 
			
		||||
    private onBlur = (ev: React.FocusEvent<HTMLInputElement>) => {
 | 
			
		||||
        this.setState({focused: false});
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -293,6 +293,7 @@ export default class RoomList2 extends React.Component<IProps, IState> {
 | 
			
		|||
                    isMinimized={this.props.isMinimized}
 | 
			
		||||
                    onResize={this.props.onResize}
 | 
			
		||||
                    extraBadTilesThatShouldntExist={extraTiles}
 | 
			
		||||
                    isFiltered={!!this.searchFilter.search}
 | 
			
		||||
                />
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,6 +78,7 @@ interface IProps {
 | 
			
		|||
    isMinimized: boolean;
 | 
			
		||||
    tagId: TagID;
 | 
			
		||||
    onResize: () => void;
 | 
			
		||||
    isFiltered: boolean;
 | 
			
		||||
 | 
			
		||||
    // TODO: Don't use this. It's for community invites, and community invites shouldn't be here.
 | 
			
		||||
    // You should feel bad if you use this.
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +93,7 @@ interface IState {
 | 
			
		|||
    notificationState: ListNotificationState;
 | 
			
		||||
    contextMenuPosition: PartialDOMRect;
 | 
			
		||||
    isResizing: boolean;
 | 
			
		||||
    isExpanded: boolean; // used for the for expand of the sublist when the room list is being filtered
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +111,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		|||
            notificationState: RoomNotificationStateStore.instance.getListState(this.props.tagId),
 | 
			
		||||
            contextMenuPosition: null,
 | 
			
		||||
            isResizing: false,
 | 
			
		||||
            isExpanded: this.props.isFiltered ? this.props.isFiltered : !this.layout.isCollapsed
 | 
			
		||||
        };
 | 
			
		||||
        this.state.notificationState.setRooms(this.props.rooms);
 | 
			
		||||
        this.dispatcherRef = defaultDispatcher.register(this.onAction);
 | 
			
		||||
| 
						 | 
				
			
			@ -123,8 +126,15 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		|||
        return Math.min(nVisible, this.numTiles);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate() {
 | 
			
		||||
    public componentDidUpdate(prevProps: Readonly<IProps>) {
 | 
			
		||||
        this.state.notificationState.setRooms(this.props.rooms);
 | 
			
		||||
        if (prevProps.isFiltered !== this.props.isFiltered) {
 | 
			
		||||
            if (this.props.isFiltered) {
 | 
			
		||||
                this.setState({isExpanded: true});
 | 
			
		||||
            } else {
 | 
			
		||||
                this.setState({isExpanded: !this.layout.isCollapsed});
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
| 
						 | 
				
			
			@ -137,10 +147,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		|||
            // XXX: we have to do this a tick later because we have incorrect intermediate props during a room change
 | 
			
		||||
            // where we lose the room we are changing from temporarily and then it comes back in an update right after.
 | 
			
		||||
            setImmediate(() => {
 | 
			
		||||
                const isCollapsed = this.layout.isCollapsed;
 | 
			
		||||
                const roomIndex = this.props.rooms.findIndex((r) => r.roomId === payload.room_id);
 | 
			
		||||
 | 
			
		||||
                if (isCollapsed && roomIndex > -1) {
 | 
			
		||||
                if (!this.state.isExpanded && roomIndex > -1) {
 | 
			
		||||
                    this.toggleCollapsed();
 | 
			
		||||
                }
 | 
			
		||||
                // extend the visible section to include the room if it is entirely invisible
 | 
			
		||||
| 
						 | 
				
			
			@ -295,24 +304,23 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    private toggleCollapsed = () => {
 | 
			
		||||
        this.layout.isCollapsed = !this.layout.isCollapsed;
 | 
			
		||||
        this.forceUpdate(); // because the layout doesn't trigger an update
 | 
			
		||||
        this.layout.isCollapsed = this.state.isExpanded;
 | 
			
		||||
        this.setState({isExpanded: !this.layout.isCollapsed});
 | 
			
		||||
        setImmediate(() => this.props.onResize()); // needs to happen when the DOM is updated
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onHeaderKeyDown = (ev: React.KeyboardEvent) => {
 | 
			
		||||
        const isCollapsed = this.layout && this.layout.isCollapsed;
 | 
			
		||||
        switch (ev.key) {
 | 
			
		||||
            case Key.ARROW_LEFT:
 | 
			
		||||
                ev.stopPropagation();
 | 
			
		||||
                if (!isCollapsed) {
 | 
			
		||||
                if (this.state.isExpanded) {
 | 
			
		||||
                    // On ARROW_LEFT collapse the room sublist if it isn't already
 | 
			
		||||
                    this.toggleCollapsed();
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case Key.ARROW_RIGHT: {
 | 
			
		||||
                ev.stopPropagation();
 | 
			
		||||
                if (isCollapsed) {
 | 
			
		||||
                if (!this.state.isExpanded) {
 | 
			
		||||
                    // On ARROW_RIGHT expand the room sublist if it isn't already
 | 
			
		||||
                    this.toggleCollapsed();
 | 
			
		||||
                } else if (this.sublistRef.current) {
 | 
			
		||||
| 
						 | 
				
			
			@ -341,7 +349,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    private renderVisibleTiles(): React.ReactElement[] {
 | 
			
		||||
        if (this.layout && this.layout.isCollapsed) {
 | 
			
		||||
        if (!this.state.isExpanded) {
 | 
			
		||||
            // don't waste time on rendering
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -498,7 +506,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		|||
 | 
			
		||||
                    const collapseClasses = classNames({
 | 
			
		||||
                        'mx_RoomSublist2_collapseBtn': true,
 | 
			
		||||
                        'mx_RoomSublist2_collapseBtn_collapsed': this.layout && this.layout.isCollapsed,
 | 
			
		||||
                        'mx_RoomSublist2_collapseBtn_collapsed': !this.state.isExpanded,
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    const classes = classNames({
 | 
			
		||||
| 
						 | 
				
			
			@ -526,7 +534,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 | 
			
		|||
                                    tabIndex={tabIndex}
 | 
			
		||||
                                    className="mx_RoomSublist2_headerText"
 | 
			
		||||
                                    role="treeitem"
 | 
			
		||||
                                    aria-expanded={!this.layout.isCollapsed}
 | 
			
		||||
                                    aria-expanded={this.state.isExpanded}
 | 
			
		||||
                                    aria-level={1}
 | 
			
		||||
                                    onClick={this.onHeaderClick}
 | 
			
		||||
                                    onContextMenu={this.onContextMenu}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,11 +55,6 @@ export class ListNotificationState extends NotificationState {
 | 
			
		|||
        for (const newRoom of diff.added) {
 | 
			
		||||
            const state = this.getRoomFn(newRoom);
 | 
			
		||||
            state.on(NOTIFICATION_STATE_UPDATE, this.onRoomNotificationStateUpdate);
 | 
			
		||||
            if (this.states[newRoom.roomId]) {
 | 
			
		||||
                // "Should never happen" disclaimer.
 | 
			
		||||
                console.warn("Overwriting notification state for room:", newRoom.roomId);
 | 
			
		||||
                this.states[newRoom.roomId].destroy();
 | 
			
		||||
            }
 | 
			
		||||
            this.states[newRoom.roomId] = state;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,17 @@ import { getListAlgorithmInstance } from "./list-ordering";
 | 
			
		|||
 */
 | 
			
		||||
export const LIST_UPDATED_EVENT = "list_updated_event";
 | 
			
		||||
 | 
			
		||||
// These are the causes which require a room to be known in order for us to handle them. If
 | 
			
		||||
// a cause in this list is raised and we don't know about the room, we don't handle the update.
 | 
			
		||||
//
 | 
			
		||||
// Note: these typically happen when a new room is coming in, such as the user creating or
 | 
			
		||||
// joining the room. For these cases, we need to know about the room prior to handling it otherwise
 | 
			
		||||
// we'll make bad assumptions.
 | 
			
		||||
const CAUSES_REQUIRING_ROOM = [
 | 
			
		||||
    RoomUpdateCause.Timeline,
 | 
			
		||||
    RoomUpdateCause.ReadReceipt,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
interface IStickyRoom {
 | 
			
		||||
    room: Room;
 | 
			
		||||
    position: number;
 | 
			
		||||
| 
						 | 
				
			
			@ -666,18 +677,6 @@ export class Algorithm extends EventEmitter {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (hasTags && isForLastSticky && !knownRoomRef) {
 | 
			
		||||
                // we have a fairly good chance at losing a room right now. Under some circumstances,
 | 
			
		||||
                // we can end up with a room which transitions references and tag changes, then gets
 | 
			
		||||
                // lost when the sticky room changes. To counter this, we try and add the room to the
 | 
			
		||||
                // list manually as the condition below to update the reference will fail.
 | 
			
		||||
                //
 | 
			
		||||
                // Other conditions *should* result in the room being sorted into the right place.
 | 
			
		||||
                console.warn(`${room.roomId} was about to be lost - inserting at end of room list`);
 | 
			
		||||
                this.rooms.push(room);
 | 
			
		||||
                knownRoomRef = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If we have tags for a room and don't have the room referenced, something went horribly
 | 
			
		||||
            // wrong - the reference should have been updated above.
 | 
			
		||||
            if (hasTags && !knownRoomRef && !isSticky) {
 | 
			
		||||
| 
						 | 
				
			
			@ -690,6 +689,13 @@ export class Algorithm extends EventEmitter {
 | 
			
		|||
                // to trigger a sticky room update ourselves.
 | 
			
		||||
                this._stickyRoom.room = room;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If after all that we're still a NewRoom update, add the room if applicable.
 | 
			
		||||
            // We don't do this for the sticky room (because it causes duplication issues)
 | 
			
		||||
            // or if we know about the reference (as it should be replaced).
 | 
			
		||||
            if (cause === RoomUpdateCause.NewRoom && !isSticky && !knownRoomRef) {
 | 
			
		||||
                this.rooms.push(room);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cause === RoomUpdateCause.PossibleTagChange) {
 | 
			
		||||
| 
						 | 
				
			
			@ -704,6 +710,7 @@ export class Algorithm extends EventEmitter {
 | 
			
		|||
                    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
 | 
			
		||||
| 
						 | 
				
			
			@ -711,6 +718,7 @@ export class Algorithm extends EventEmitter {
 | 
			
		|||
                    const algorithm: OrderingAlgorithm = this.algorithms[addTag];
 | 
			
		||||
                    if (!algorithm) throw new Error(`No algorithm for ${addTag}`);
 | 
			
		||||
                    await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom);
 | 
			
		||||
                    this.cachedRooms[addTag] = algorithm.orderedRooms;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Update the tag map so we don't regen it in a moment
 | 
			
		||||
| 
						 | 
				
			
			@ -755,6 +763,11 @@ export class Algorithm extends EventEmitter {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        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`);
 | 
			
		||||
                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})`);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -160,7 +160,10 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
 | 
			
		|||
            this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
 | 
			
		||||
        } else if (cause === RoomUpdateCause.RoomRemoved) {
 | 
			
		||||
            const roomIdx = this.getRoomIndex(room);
 | 
			
		||||
            if (roomIdx === -1) return false; // no change
 | 
			
		||||
            if (roomIdx === -1) {
 | 
			
		||||
                console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`);
 | 
			
		||||
                return false; // no change
 | 
			
		||||
            }
 | 
			
		||||
            const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices);
 | 
			
		||||
            this.alterCategoryPositionBy(oldCategory, -1, this.indices);
 | 
			
		||||
            this.cachedOrderedRooms.splice(roomIdx, 1); // remove the room
 | 
			
		||||
| 
						 | 
				
			
			@ -169,15 +172,6 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private getRoomIndex(room: Room): number {
 | 
			
		||||
        let roomIdx = this.cachedOrderedRooms.indexOf(room);
 | 
			
		||||
        if (roomIdx === -1) { // can only happen if the js-sdk's store goes sideways.
 | 
			
		||||
            console.warn(`Degrading performance to find missing room in "${this.tagId}": ${room.roomId}`);
 | 
			
		||||
            roomIdx = this.cachedOrderedRooms.findIndex(r => r.roomId === room.roomId);
 | 
			
		||||
        }
 | 
			
		||||
        return roomIdx;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
 | 
			
		||||
        try {
 | 
			
		||||
            await this.updateLock.acquireAsync();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,8 +50,12 @@ export class NaturalAlgorithm extends OrderingAlgorithm {
 | 
			
		|||
            if (cause === RoomUpdateCause.NewRoom) {
 | 
			
		||||
                this.cachedOrderedRooms.push(room);
 | 
			
		||||
            } else if (cause === RoomUpdateCause.RoomRemoved) {
 | 
			
		||||
                const idx = this.cachedOrderedRooms.indexOf(room);
 | 
			
		||||
                if (idx >= 0) this.cachedOrderedRooms.splice(idx, 1);
 | 
			
		||||
                const idx = this.getRoomIndex(room);
 | 
			
		||||
                if (idx >= 0) {
 | 
			
		||||
                    this.cachedOrderedRooms.splice(idx, 1);
 | 
			
		||||
                } else {
 | 
			
		||||
                    console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // TODO: Optimize this to avoid useless operations: https://github.com/vector-im/riot-web/issues/14035
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,4 +70,13 @@ export abstract class OrderingAlgorithm {
 | 
			
		|||
     * @returns True if the update requires the Algorithm to update the presentation layers.
 | 
			
		||||
     */
 | 
			
		||||
    public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean>;
 | 
			
		||||
 | 
			
		||||
    protected getRoomIndex(room: Room): number {
 | 
			
		||||
        let roomIdx = this.cachedOrderedRooms.indexOf(room);
 | 
			
		||||
        if (roomIdx === -1) { // can only happen if the js-sdk's store goes sideways.
 | 
			
		||||
            console.warn(`Degrading performance to find missing room in "${this.tagId}": ${room.roomId}`);
 | 
			
		||||
            roomIdx = this.cachedOrderedRooms.findIndex(r => r.roomId === room.roomId);
 | 
			
		||||
        }
 | 
			
		||||
        return roomIdx;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue