Merge remote-tracking branch 'origin/develop' into element
commit
ab51dd28dc
|
@ -24,7 +24,7 @@ limitations under the License.
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_NotificationBadge {
|
.mx_NotificationBadge, .mx_RoomTile2_badgeContainer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
|
@ -89,7 +89,6 @@ limitations under the License.
|
||||||
height: 16px;
|
height: 16px;
|
||||||
// don't set width so that it takes no space when there is no badge to show
|
// don't set width so that it takes no space when there is no badge to show
|
||||||
margin: auto 0; // vertically align
|
margin: auto 0; // vertically align
|
||||||
position: relative; // fixes badge alignment in some scenarios
|
|
||||||
|
|
||||||
// Create a flexbox to make aligning dot badges easier
|
// Create a flexbox to make aligning dot badges easier
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -105,7 +105,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
||||||
ev.target.select();
|
ev.target.select();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onBlur = () => {
|
private onBlur = (ev: React.FocusEvent<HTMLInputElement>) => {
|
||||||
this.setState({focused: false});
|
this.setState({focused: false});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,7 @@ export default class RoomList2 extends React.Component<IProps, IState> {
|
||||||
isMinimized={this.props.isMinimized}
|
isMinimized={this.props.isMinimized}
|
||||||
onResize={this.props.onResize}
|
onResize={this.props.onResize}
|
||||||
extraBadTilesThatShouldntExist={extraTiles}
|
extraBadTilesThatShouldntExist={extraTiles}
|
||||||
|
isFiltered={!!this.searchFilter.search}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ interface IProps {
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
tagId: TagID;
|
tagId: TagID;
|
||||||
onResize: () => void;
|
onResize: () => void;
|
||||||
|
isFiltered: boolean;
|
||||||
|
|
||||||
// TODO: Don't use this. It's for community invites, and community invites shouldn't be here.
|
// 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.
|
// You should feel bad if you use this.
|
||||||
|
@ -92,6 +93,7 @@ interface IState {
|
||||||
notificationState: ListNotificationState;
|
notificationState: ListNotificationState;
|
||||||
contextMenuPosition: PartialDOMRect;
|
contextMenuPosition: PartialDOMRect;
|
||||||
isResizing: boolean;
|
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> {
|
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),
|
notificationState: RoomNotificationStateStore.instance.getListState(this.props.tagId),
|
||||||
contextMenuPosition: null,
|
contextMenuPosition: null,
|
||||||
isResizing: false,
|
isResizing: false,
|
||||||
|
isExpanded: this.props.isFiltered ? this.props.isFiltered : !this.layout.isCollapsed
|
||||||
};
|
};
|
||||||
this.state.notificationState.setRooms(this.props.rooms);
|
this.state.notificationState.setRooms(this.props.rooms);
|
||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
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);
|
return Math.min(nVisible, this.numTiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate() {
|
public componentDidUpdate(prevProps: Readonly<IProps>) {
|
||||||
this.state.notificationState.setRooms(this.props.rooms);
|
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() {
|
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
|
// 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.
|
// where we lose the room we are changing from temporarily and then it comes back in an update right after.
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
const isCollapsed = this.layout.isCollapsed;
|
|
||||||
const roomIndex = this.props.rooms.findIndex((r) => r.roomId === payload.room_id);
|
const roomIndex = this.props.rooms.findIndex((r) => r.roomId === payload.room_id);
|
||||||
|
|
||||||
if (isCollapsed && roomIndex > -1) {
|
if (!this.state.isExpanded && roomIndex > -1) {
|
||||||
this.toggleCollapsed();
|
this.toggleCollapsed();
|
||||||
}
|
}
|
||||||
// extend the visible section to include the room if it is entirely invisible
|
// 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 = () => {
|
private toggleCollapsed = () => {
|
||||||
this.layout.isCollapsed = !this.layout.isCollapsed;
|
this.layout.isCollapsed = this.state.isExpanded;
|
||||||
this.forceUpdate(); // because the layout doesn't trigger an update
|
this.setState({isExpanded: !this.layout.isCollapsed});
|
||||||
setImmediate(() => this.props.onResize()); // needs to happen when the DOM is updated
|
setImmediate(() => this.props.onResize()); // needs to happen when the DOM is updated
|
||||||
};
|
};
|
||||||
|
|
||||||
private onHeaderKeyDown = (ev: React.KeyboardEvent) => {
|
private onHeaderKeyDown = (ev: React.KeyboardEvent) => {
|
||||||
const isCollapsed = this.layout && this.layout.isCollapsed;
|
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case Key.ARROW_LEFT:
|
case Key.ARROW_LEFT:
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (!isCollapsed) {
|
if (this.state.isExpanded) {
|
||||||
// On ARROW_LEFT collapse the room sublist if it isn't already
|
// On ARROW_LEFT collapse the room sublist if it isn't already
|
||||||
this.toggleCollapsed();
|
this.toggleCollapsed();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Key.ARROW_RIGHT: {
|
case Key.ARROW_RIGHT: {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (isCollapsed) {
|
if (!this.state.isExpanded) {
|
||||||
// On ARROW_RIGHT expand the room sublist if it isn't already
|
// On ARROW_RIGHT expand the room sublist if it isn't already
|
||||||
this.toggleCollapsed();
|
this.toggleCollapsed();
|
||||||
} else if (this.sublistRef.current) {
|
} else if (this.sublistRef.current) {
|
||||||
|
@ -341,7 +349,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderVisibleTiles(): React.ReactElement[] {
|
private renderVisibleTiles(): React.ReactElement[] {
|
||||||
if (this.layout && this.layout.isCollapsed) {
|
if (!this.state.isExpanded) {
|
||||||
// don't waste time on rendering
|
// don't waste time on rendering
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -498,7 +506,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
const collapseClasses = classNames({
|
const collapseClasses = classNames({
|
||||||
'mx_RoomSublist2_collapseBtn': true,
|
'mx_RoomSublist2_collapseBtn': true,
|
||||||
'mx_RoomSublist2_collapseBtn_collapsed': this.layout && this.layout.isCollapsed,
|
'mx_RoomSublist2_collapseBtn_collapsed': !this.state.isExpanded,
|
||||||
});
|
});
|
||||||
|
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
|
@ -526,7 +534,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
className="mx_RoomSublist2_headerText"
|
className="mx_RoomSublist2_headerText"
|
||||||
role="treeitem"
|
role="treeitem"
|
||||||
aria-expanded={!this.layout.isCollapsed}
|
aria-expanded={this.state.isExpanded}
|
||||||
aria-level={1}
|
aria-level={1}
|
||||||
onClick={this.onHeaderClick}
|
onClick={this.onHeaderClick}
|
||||||
onContextMenu={this.onContextMenu}
|
onContextMenu={this.onContextMenu}
|
||||||
|
|
|
@ -55,11 +55,6 @@ export class ListNotificationState extends NotificationState {
|
||||||
for (const newRoom of diff.added) {
|
for (const newRoom of diff.added) {
|
||||||
const state = this.getRoomFn(newRoom);
|
const state = this.getRoomFn(newRoom);
|
||||||
state.on(NOTIFICATION_STATE_UPDATE, this.onRoomNotificationStateUpdate);
|
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;
|
this.states[newRoom.roomId] = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,17 @@ import { getListAlgorithmInstance } from "./list-ordering";
|
||||||
*/
|
*/
|
||||||
export const LIST_UPDATED_EVENT = "list_updated_event";
|
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 {
|
interface IStickyRoom {
|
||||||
room: Room;
|
room: Room;
|
||||||
position: number;
|
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
|
// 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.
|
// wrong - the reference should have been updated above.
|
||||||
if (hasTags && !knownRoomRef && !isSticky) {
|
if (hasTags && !knownRoomRef && !isSticky) {
|
||||||
|
@ -690,6 +689,13 @@ export class Algorithm extends EventEmitter {
|
||||||
// to trigger a sticky room update ourselves.
|
// to trigger a sticky room update ourselves.
|
||||||
this._stickyRoom.room = room;
|
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) {
|
if (cause === RoomUpdateCause.PossibleTagChange) {
|
||||||
|
@ -704,6 +710,7 @@ export class Algorithm extends EventEmitter {
|
||||||
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;
|
||||||
}
|
}
|
||||||
for (const addTag of diff.added) {
|
for (const addTag of diff.added) {
|
||||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
// 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];
|
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);
|
||||||
|
this.cachedRooms[addTag] = algorithm.orderedRooms;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -755,6 +763,11 @@ export class Algorithm extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.roomIdsToTags[room.roomId]) {
|
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
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||||
console.log(`[RoomListDebug] Updating tags for room ${room.roomId} (${room.name})`);
|
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)
|
this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
|
||||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||||
const roomIdx = this.getRoomIndex(room);
|
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);
|
const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices);
|
||||||
this.alterCategoryPositionBy(oldCategory, -1, this.indices);
|
this.alterCategoryPositionBy(oldCategory, -1, this.indices);
|
||||||
this.cachedOrderedRooms.splice(roomIdx, 1); // remove the room
|
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> {
|
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await this.updateLock.acquireAsync();
|
await this.updateLock.acquireAsync();
|
||||||
|
|
|
@ -50,8 +50,12 @@ export class NaturalAlgorithm extends OrderingAlgorithm {
|
||||||
if (cause === RoomUpdateCause.NewRoom) {
|
if (cause === RoomUpdateCause.NewRoom) {
|
||||||
this.cachedOrderedRooms.push(room);
|
this.cachedOrderedRooms.push(room);
|
||||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||||
const idx = this.cachedOrderedRooms.indexOf(room);
|
const idx = this.getRoomIndex(room);
|
||||||
if (idx >= 0) this.cachedOrderedRooms.splice(idx, 1);
|
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
|
// 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.
|
* @returns True if the update requires the Algorithm to update the presentation layers.
|
||||||
*/
|
*/
|
||||||
public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean>;
|
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