Show suggested rooms from the selected space

This commit is contained in:
Michael Telatynski 2021-03-08 15:52:21 +00:00
parent ab4220b20d
commit 6a5efad142
5 changed files with 116 additions and 14 deletions

View file

@ -19,6 +19,7 @@ limitations under the License.
import * as React from "react";
import { Dispatcher } from "flux";
import { Room } from "matrix-js-sdk/src/models/room";
import * as fbEmitter from "fbemitter";
import { _t, _td } from "../../../languageHandler";
import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
@ -47,9 +48,11 @@ import { IconizedContextMenuOption, IconizedContextMenuOptionList } from "../con
import AccessibleButton from "../elements/AccessibleButton";
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
import CallHandler from "../../../CallHandler";
import SpaceStore from "../../../stores/SpaceStore";
import SpaceStore, { SUGGESTED_ROOMS } from "../../../stores/SpaceStore";
import { showAddExistingRooms, showCreateNewRoom } from "../../../utils/space";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { ISpaceSummaryRoom } from "../../structures/SpaceRoomDirectory";
import RoomAvatar from "../avatars/RoomAvatar";
interface IProps {
onKeyDown: (ev: React.KeyboardEvent) => void;
@ -63,6 +66,8 @@ interface IProps {
interface IState {
sublists: ITagMap;
isNameFiltering: boolean;
currentRoomId?: string;
suggestedRooms: ISpaceSummaryRoom[];
}
const TAG_ORDER: TagID[] = [
@ -75,6 +80,7 @@ const TAG_ORDER: TagID[] = [
DefaultTagID.LowPriority,
DefaultTagID.ServerNotice,
DefaultTagID.Suggested,
DefaultTagID.Archived,
];
const CUSTOM_TAGS_BEFORE_TAG = DefaultTagID.LowPriority;
@ -242,6 +248,12 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
isInvite: false,
defaultHidden: true,
},
[DefaultTagID.Suggested]: {
sectionLabel: _td("Suggested Rooms"),
isInvite: false,
defaultHidden: false,
},
};
function customTagAesthetics(tagId: TagID): ITagAesthetics {
@ -260,6 +272,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
private dispatcherRef;
private customTagStoreRef;
private tagAesthetics: ITagAestheticsMap;
private roomStoreToken: fbEmitter.EventSubscription;
constructor(props: IProps) {
super(props);
@ -267,6 +280,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
this.state = {
sublists: {},
isNameFiltering: !!RoomListStore.instance.getFirstNameFilterCondition(),
suggestedRooms: SpaceStore.instance.suggestedRooms,
};
// shallow-copy from the template as we need to make modifications to it
@ -274,20 +288,30 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
this.updateDmAddRoomAction();
this.dispatcherRef = defaultDispatcher.register(this.onAction);
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
}
public componentDidMount(): void {
SpaceStore.instance.on(SUGGESTED_ROOMS, this.updateSuggestedRooms);
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.updateLists);
this.customTagStoreRef = CustomRoomTagStore.addListener(this.updateLists);
this.updateLists(); // trigger the first update
}
public componentWillUnmount() {
SpaceStore.instance.off(SUGGESTED_ROOMS, this.updateSuggestedRooms);
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
defaultDispatcher.unregister(this.dispatcherRef);
if (this.customTagStoreRef) this.customTagStoreRef.remove();
if (this.roomStoreToken) this.roomStoreToken.remove();
}
private onRoomViewStoreUpdate = () => {
this.setState({
currentRoomId: RoomViewStore.getRoomId(),
});
};
private updateDmAddRoomAction() {
const dmTagAesthetics = objectShallowClone(TAG_AESTHETICS[DefaultTagID.DM]);
if (CallHandler.sharedInstance().getSupportsPstnProtocol()) {
@ -319,7 +343,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
private getRoomDelta = (roomId: string, delta: number, unread = false) => {
const lists = RoomListStore.instance.orderedLists;
const rooms: Room = [];
const rooms: Room[] = [];
TAG_ORDER.forEach(t => {
let listRooms = lists[t];
@ -340,6 +364,10 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
return room;
};
private updateSuggestedRooms = (suggestedRooms: ISpaceSummaryRoom[]) => {
this.setState({ suggestedRooms });
};
private updateLists = () => {
const newLists = RoomListStore.instance.orderedLists;
if (SettingsStore.getValue("advancedRoomListLogging")) {
@ -394,6 +422,39 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
dis.dispatch({ action: Action.ViewRoomDirectory, initialText });
};
private renderSuggestedRooms(): JSX.Element[] {
return this.state.suggestedRooms.map(room => {
const name = room.name || room.canonical_alias || room.aliases.pop() || _t("Empty room");
const avatar = (
<RoomAvatar
oobData={{
name,
avatarUrl: room.avatar_url,
}}
width={32}
height={32}
resizeMethod="crop"
/>
);
const viewRoom = () => {
defaultDispatcher.dispatch({
action: "view_room",
room_id: room.room_id,
});
};
return (
<TemporaryTile
isMinimized={this.props.isMinimized}
isSelected={this.state.currentRoomId === room.room_id}
displayName={name}
avatar={avatar}
onClick={viewRoom}
key={`suggestedRoomTile_${room.room_id}`}
/>
);
});
}
private renderCommunityInvites(): TemporaryTile[] {
// TODO: Put community invites in a more sensible place (not in the room list)
// See https://github.com/vector-im/element-web/issues/14456
@ -447,7 +508,14 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
for (const orderedTagId of tagOrder) {
const orderedRooms = this.state.sublists[orderedTagId] || [];
const extraTiles = orderedTagId === DefaultTagID.Invite ? this.renderCommunityInvites() : null;
let extraTiles = null;
if (orderedTagId === DefaultTagID.Invite) {
extraTiles = this.renderCommunityInvites();
} else if (orderedTagId === DefaultTagID.Suggested) {
extraTiles = this.renderSuggestedRooms();
}
const totalTiles = orderedRooms.length + (extraTiles ? extraTiles.length : 0);
if (totalTiles === 0 && !ALWAYS_VISIBLE_TAGS.includes(orderedTagId)) {
continue; // skip tag - not needed

View file

@ -28,7 +28,7 @@ interface IProps {
isSelected: boolean;
displayName: string;
avatar: React.ReactElement;
notificationState: NotificationState;
notificationState?: NotificationState;
onClick: () => void;
}
@ -63,12 +63,15 @@ export default class TemporaryTile extends React.Component<IProps, IState> {
'mx_RoomTile_minimized': this.props.isMinimized,
});
const badge = (
<NotificationBadge
notification={this.props.notificationState}
forceCount={false}
/>
);
let badge;
if (this.props.notificationState) {
badge = (
<NotificationBadge
notification={this.props.notificationState}
forceCount={false}
/>
);
}
let name = this.props.displayName;
if (typeof name !== 'string') name = '';
@ -76,7 +79,7 @@ export default class TemporaryTile extends React.Component<IProps, IState> {
const nameClasses = classNames({
"mx_RoomTile_name": true,
"mx_RoomTile_nameHasUnreadEvents": this.props.notificationState.isUnread,
"mx_RoomTile_nameHasUnreadEvents": this.props.notificationState?.isUnread,
});
let nameContainer = (