Initial breakout for room list rewrite

This does a number of things (sorry):
* Estimates the type changes needed to the dispatcher (later to be replaced by https://github.com/matrix-org/matrix-react-sdk/pull/4593)
* Sets up the stack for a whole new room list store, and later components for usage.
* Create a proxy class to ensure the app still functions as expected when the various stores are enabled/disabled
* Demonstrates a possible structure for algorithms
This commit is contained in:
Travis Ralston 2020-03-20 14:38:20 -06:00
parent 82b55ffd77
commit 08419d195e
21 changed files with 794 additions and 47 deletions

View file

@ -26,6 +26,7 @@ import * as VectorConferenceHandler from '../../VectorConferenceHandler';
import SettingsStore from '../../settings/SettingsStore';
import {_t} from "../../languageHandler";
import Analytics from "../../Analytics";
import RoomList2 from "../views/rooms/RoomList2";
const LeftPanel = createReactClass({
@ -273,6 +274,29 @@ const LeftPanel = createReactClass({
breadcrumbs = (<RoomBreadcrumbs collapsed={this.props.collapsed} />);
}
let roomList = null;
if (SettingsStore.isFeatureEnabled("feature_new_room_list")) {
roomList = <RoomList2
onKeyDown={this._onKeyDown}
resizeNotifier={this.props.resizeNotifier}
collapsed={this.props.collapsed}
searchFilter={this.state.searchFilter}
ref={this.collectRoomList}
onFocus={this._onFocus}
onBlur={this._onBlur}
/>;
} else {
roomList = <RoomList
onKeyDown={this._onKeyDown}
onFocus={this._onFocus}
onBlur={this._onBlur}
ref={this.collectRoomList}
resizeNotifier={this.props.resizeNotifier}
collapsed={this.props.collapsed}
searchFilter={this.state.searchFilter}
ConferenceHandler={VectorConferenceHandler} />;
}
return (
<div className={containerClasses}>
{ tagPanelContainer }
@ -284,15 +308,7 @@ const LeftPanel = createReactClass({
{ exploreButton }
{ searchBox }
</div>
<RoomList
onKeyDown={this._onKeyDown}
onFocus={this._onFocus}
onBlur={this._onBlur}
ref={this.collectRoomList}
resizeNotifier={this.props.resizeNotifier}
collapsed={this.props.collapsed}
searchFilter={this.state.searchFilter}
ConferenceHandler={VectorConferenceHandler} />
{roomList}
</aside>
</div>
);

View file

@ -31,7 +31,6 @@ import dis from '../../dispatcher';
import sessionStore from '../../stores/SessionStore';
import {MatrixClientPeg, MatrixClientCreds} from '../../MatrixClientPeg';
import SettingsStore from "../../settings/SettingsStore";
import RoomListStore from "../../stores/RoomListStore";
import TagOrderActions from '../../actions/TagOrderActions';
import RoomListActions from '../../actions/RoomListActions';
@ -42,6 +41,8 @@ import * as KeyboardShortcuts from "../../accessibility/KeyboardShortcuts";
import HomePage from "./HomePage";
import ResizeNotifier from "../../utils/ResizeNotifier";
import PlatformPeg from "../../PlatformPeg";
import { RoomListStoreTempProxy } from "../../stores/room-list/RoomListStoreTempProxy";
import { DefaultTagID } from "../../stores/room-list/models";
// We need to fetch each pinned message individually (if we don't already have it)
// so each pinned message may trigger a request. Limit the number per room for sanity.
// NB. this is just for server notices rather than pinned messages in general.
@ -297,18 +298,18 @@ class LoggedInView extends React.PureComponent<IProps, IState> {
};
onRoomStateEvents = (ev, state) => {
const roomLists = RoomListStore.getRoomLists();
if (roomLists['m.server_notice'] && roomLists['m.server_notice'].some(r => r.roomId === ev.getRoomId())) {
const roomLists = RoomListStoreTempProxy.getRoomLists();
if (roomLists[DefaultTagID.ServerNotice] && roomLists[DefaultTagID.ServerNotice].some(r => r.roomId === ev.getRoomId())) {
this._updateServerNoticeEvents();
}
};
_updateServerNoticeEvents = async () => {
const roomLists = RoomListStore.getRoomLists();
if (!roomLists['m.server_notice']) return [];
const roomLists = RoomListStoreTempProxy.getRoomLists();
if (!roomLists[DefaultTagID.ServerNotice]) return [];
const pinnedEvents = [];
for (const room of roomLists['m.server_notice']) {
for (const room of roomLists[DefaultTagID.ServerNotice]) {
const pinStateEvent = room.currentState.getStateEvents("m.room.pinned_events", "");
if (!pinStateEvent || !pinStateEvent.getContent().pinned) continue;

View file

@ -34,8 +34,9 @@ import {humanizeTime} from "../../../utils/humanize";
import createRoom, {canEncryptToAllUsers} from "../../../createRoom";
import {inviteMultipleToRoom} from "../../../RoomInvite";
import SettingsStore from '../../../settings/SettingsStore';
import RoomListStore, {TAG_DM} from "../../../stores/RoomListStore";
import {Key} from "../../../Keyboard";
import {RoomListStoreTempProxy} from "../../../stores/room-list/RoomListStoreTempProxy";
import {DefaultTagID} from "../../../stores/room-list/models";
export const KIND_DM = "dm";
export const KIND_INVITE = "invite";
@ -343,10 +344,10 @@ export default class InviteDialog extends React.PureComponent {
_buildRecents(excludedTargetIds: Set<string>): {userId: string, user: RoomMember, lastActive: number} {
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room
// Also pull in all the rooms tagged as TAG_DM so we don't miss anything. Sometimes the
// Also pull in all the rooms tagged as DefaultTagID.DM so we don't miss anything. Sometimes the
// room list doesn't tag the room for the DMRoomMap, but does for the room list.
const taggedRooms = RoomListStore.getRoomLists();
const dmTaggedRooms = taggedRooms[TAG_DM];
const taggedRooms = RoomListStoreTempProxy.getRoomLists();
const dmTaggedRooms = taggedRooms[DefaultTagID.DM];
const myUserId = MatrixClientPeg.get().getUserId();
for (const dmRoom of dmTaggedRooms) {
const otherMembers = dmRoom.getJoinedMembers().filter(u => u.userId !== myUserId);

View file

@ -29,7 +29,6 @@ import rate_limited_func from "../../../ratelimitedfunc";
import * as Rooms from '../../../Rooms';
import DMRoomMap from '../../../utils/DMRoomMap';
import TagOrderStore from '../../../stores/TagOrderStore';
import RoomListStore, {TAG_DM} from '../../../stores/RoomListStore';
import CustomRoomTagStore from '../../../stores/CustomRoomTagStore';
import GroupStore from '../../../stores/GroupStore';
import RoomSubList from '../../structures/RoomSubList';
@ -41,6 +40,8 @@ import * as Receipt from "../../../utils/Receipt";
import {Resizer} from '../../../resizer';
import {Layout, Distributor} from '../../../resizer/distributors/roomsublist2';
import {RovingTabIndexProvider} from "../../../accessibility/RovingTabIndex";
import {RoomListStoreTempProxy} from "../../../stores/room-list/RoomListStoreTempProxy";
import {DefaultTagID} from "../../../stores/room-list/models";
import * as Unread from "../../../Unread";
import RoomViewStore from "../../../stores/RoomViewStore";
@ -161,7 +162,7 @@ export default createReactClass({
this.updateVisibleRooms();
});
this._roomListStoreToken = RoomListStore.addListener(() => {
this._roomListStoreToken = RoomListStoreTempProxy.addListener(() => {
this._delayedRefreshRoomList();
});
@ -521,7 +522,7 @@ export default createReactClass({
},
getTagNameForRoomId: function(roomId) {
const lists = RoomListStore.getRoomLists();
const lists = RoomListStoreTempProxy.getRoomLists();
for (const tagName of Object.keys(lists)) {
for (const room of lists[tagName]) {
// Should be impossible, but guard anyways.
@ -541,7 +542,7 @@ export default createReactClass({
},
getRoomLists: function() {
const lists = RoomListStore.getRoomLists();
const lists = RoomListStoreTempProxy.getRoomLists();
const filteredLists = {};
@ -773,10 +774,10 @@ export default createReactClass({
incomingCall: incomingCallIfTaggedAs('m.favourite'),
},
{
list: this.state.lists[TAG_DM],
list: this.state.lists[DefaultTagID.DM],
label: _t('Direct Messages'),
tagName: TAG_DM,
incomingCall: incomingCallIfTaggedAs(TAG_DM),
tagName: DefaultTagID.DM,
incomingCall: incomingCallIfTaggedAs(DefaultTagID.DM),
onAddRoom: () => {dis.dispatch({action: 'view_create_chat'});},
addRoomLabel: _t("Start chat"),
},

View file

@ -0,0 +1,130 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017, 2018 Vector Creations Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as React from "react";
import { _t } from "../../../languageHandler";
import { Layout } from '../../../resizer/distributors/roomsublist2';
import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
import { ResizeNotifier } from "../../../utils/ResizeNotifier";
import RoomListStore from "../../../stores/room-list/RoomListStore2";
interface IProps {
onKeyDown: (ev: React.KeyboardEvent) => void;
onFocus: (ev: React.FocusEvent) => void;
onBlur: (ev: React.FocusEvent) => void;
resizeNotifier: ResizeNotifier;
collapsed: boolean;
searchFilter: string;
}
interface IState {
}
// TODO: Actually write stub
export class RoomSublist2 extends React.Component<any, any> {
public setHeight(size: number) {
}
}
export default class RoomList2 extends React.Component<IProps, IState> {
private sublistRefs: { [tagId: string]: React.RefObject<RoomSublist2> } = {};
private sublistSizes: { [tagId: string]: number } = {};
private sublistCollapseStates: { [tagId: string]: boolean } = {};
private unfilteredLayout: Layout;
private filteredLayout: Layout;
constructor(props: IProps) {
super(props);
this.loadSublistSizes();
this.prepareLayouts();
}
public componentDidMount(): void {
RoomListStore.instance.addListener(() => {
console.log(RoomListStore.instance.orderedLists);
});
}
private loadSublistSizes() {
const sizesJson = window.localStorage.getItem("mx_roomlist_sizes");
if (sizesJson) this.sublistSizes = JSON.parse(sizesJson);
const collapsedJson = window.localStorage.getItem("mx_roomlist_collapsed");
if (collapsedJson) this.sublistCollapseStates = JSON.parse(collapsedJson);
}
private saveSublistSizes() {
window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.sublistSizes));
window.localStorage.setItem("mx_roomlist_collapsed", JSON.stringify(this.sublistCollapseStates));
}
private prepareLayouts() {
this.unfilteredLayout = new Layout((tagId: string, height: number) => {
const sublist = this.sublistRefs[tagId];
if (sublist) sublist.current.setHeight(height);
// TODO: Check overflow
// Don't store a height for collapsed sublists
if (!this.sublistCollapseStates[tagId]) {
this.sublistSizes[tagId] = height;
this.saveSublistSizes();
}
}, this.sublistSizes, this.sublistCollapseStates, {
allowWhitespace: false,
handleHeight: 1,
});
this.filteredLayout = new Layout((tagId: string, height: number) => {
const sublist = this.sublistRefs[tagId];
if (sublist) sublist.current.setHeight(height);
}, null, null, {
allowWhitespace: false,
handleHeight: 0,
});
}
private collectSublistRef(tagId: string, ref: React.RefObject<RoomSublist2>) {
if (!ref) {
delete this.sublistRefs[tagId];
} else {
this.sublistRefs[tagId] = ref;
}
}
public render() {
return (
<RovingTabIndexProvider handleHomeEnd={true} onKeyDown={this.props.onKeyDown}>
{({onKeyDownHandler}) => (
<div
onFocus={this.props.onFocus}
onBlur={this.props.onBlur}
onKeyDown={onKeyDownHandler}
className="mx_RoomList"
role="tree"
aria-label={_t("Rooms")}
// Firefox sometimes makes this element focusable due to
// overflow:scroll;, so force it out of tab order.
tabIndex={-1}
>{_t("TODO")}</div>
)}
</RovingTabIndexProvider>
);
}
}