mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 05:04:57 +08:00
Merge remote-tracking branch 'origin/develop' into element
This commit is contained in:
commit
ab51dd28dc
@ -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
Block a user