Prevent layout trashing when resizing the window

This commit is contained in:
Germain Souquet 2021-05-25 09:50:09 +01:00
parent 2710062df7
commit ac93cc514f
6 changed files with 11 additions and 43 deletions

View file

@ -90,10 +90,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
this.groupFilterPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => { this.groupFilterPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => {
this.setState({showGroupFilterPanel: SettingsStore.getValue("TagPanel.enableTagPanel")}); this.setState({showGroupFilterPanel: SettingsStore.getValue("TagPanel.enableTagPanel")});
}); });
// We watch the middle panel because we don't actually get resized, the middle panel does.
// We listen to the noisy channel to avoid choppy reaction times.
this.props.resizeNotifier.on("middlePanelResizedNoisy", this.onResize);
} }
public componentWillUnmount() { public componentWillUnmount() {
@ -103,7 +99,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate); RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
OwnProfileStore.instance.off(UPDATE_EVENT, this.onBackgroundImageUpdate); OwnProfileStore.instance.off(UPDATE_EVENT, this.onBackgroundImageUpdate);
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace); SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
this.props.resizeNotifier.off("middlePanelResizedNoisy", this.onResize);
} }
private updateActiveSpace = (activeSpace: Room) => { private updateActiveSpace = (activeSpace: Room) => {
@ -281,11 +276,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
this.handleStickyHeaders(list); this.handleStickyHeaders(list);
}; };
private onResize = () => {
if (!this.listContainerRef.current) return; // ignore: no headers to sticky
this.handleStickyHeaders(this.listContainerRef.current);
};
private onFocus = (ev: React.FocusEvent) => { private onFocus = (ev: React.FocusEvent) => {
this.focusedElement = ev.target; this.focusedElement = ev.target;
}; };
@ -420,7 +410,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
onFocus={this.onFocus} onFocus={this.onFocus}
onBlur={this.onBlur} onBlur={this.onBlur}
isMinimized={this.props.isMinimized} isMinimized={this.props.isMinimized}
onResize={this.onResize}
activeSpace={this.state.activeSpace} activeSpace={this.state.activeSpace}
/>; />;
@ -454,7 +443,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
{roomList} {roomList}
</div> </div>
</div> </div>
{ !this.props.isMinimized && <LeftPanelWidget onResize={this.onResize} /> } { !this.props.isMinimized && <LeftPanelWidget /> }
</aside> </aside>
</div> </div>
); );

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {useContext, useEffect, useMemo} from "react"; import React, {useContext, useMemo} from "react";
import {Resizable} from "re-resizable"; import {Resizable} from "re-resizable";
import classNames from "classnames"; import classNames from "classnames";
@ -28,15 +28,11 @@ import {useAccountData} from "../../hooks/useAccountData";
import AppTile from "../views/elements/AppTile"; import AppTile from "../views/elements/AppTile";
import {useSettingValue} from "../../hooks/useSettings"; import {useSettingValue} from "../../hooks/useSettings";
interface IProps {
onResize(): void;
}
const MIN_HEIGHT = 100; const MIN_HEIGHT = 100;
const MAX_HEIGHT = 500; // or 50% of the window height const MAX_HEIGHT = 500; // or 50% of the window height
const INITIAL_HEIGHT = 280; const INITIAL_HEIGHT = 280;
const LeftPanelWidget: React.FC<IProps> = ({ onResize }) => { const LeftPanelWidget: React.FC = () => {
const cli = useContext(MatrixClientContext); const cli = useContext(MatrixClientContext);
const mWidgetsEvent = useAccountData<Record<string, IWidgetEvent>>(cli, "m.widgets"); const mWidgetsEvent = useAccountData<Record<string, IWidgetEvent>>(cli, "m.widgets");
@ -56,7 +52,6 @@ const LeftPanelWidget: React.FC<IProps> = ({ onResize }) => {
const [height, setHeight] = useLocalStorageState("left-panel-widget-height", INITIAL_HEIGHT); const [height, setHeight] = useLocalStorageState("left-panel-widget-height", INITIAL_HEIGHT);
const [expanded, setExpanded] = useLocalStorageState("left-panel-widget-expanded", true); const [expanded, setExpanded] = useLocalStorageState("left-panel-widget-expanded", true);
useEffect(onResize, [expanded, onResize]);
const [onFocus, isActive, ref] = useRovingTabIndex(); const [onFocus, isActive, ref] = useRovingTabIndex();
const tabIndex = isActive ? 0 : -1; const tabIndex = isActive ? 0 : -1;
@ -69,7 +64,6 @@ const LeftPanelWidget: React.FC<IProps> = ({ onResize }) => {
size={{height} as any} size={{height} as any}
minHeight={MIN_HEIGHT} minHeight={MIN_HEIGHT}
maxHeight={Math.min(window.innerHeight / 2, MAX_HEIGHT)} maxHeight={Math.min(window.innerHeight / 2, MAX_HEIGHT)}
onResize={onResize}
onResizeStop={(e, dir, ref, d) => { onResizeStop={(e, dir, ref, d) => {
setHeight(height + d.height); setHeight(height + d.height);
}} }}

View file

@ -87,6 +87,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
import SecurityCustomisations from "../../customisations/Security"; import SecurityCustomisations from "../../customisations/Security";
import PerformanceMonitor, { PerformanceEntryNames } from "../../performance"; import PerformanceMonitor, { PerformanceEntryNames } from "../../performance";
import UIStore, { UI_EVENTS } from "../../stores/UIStore";
/** constants for MatrixChat.state.view */ /** constants for MatrixChat.state.view */
export enum Views { export enum Views {
@ -225,7 +226,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
firstSyncPromise: IDeferred<void>; firstSyncPromise: IDeferred<void>;
private screenAfterLogin?: IScreen; private screenAfterLogin?: IScreen;
private windowWidth: number;
private pageChanging: boolean; private pageChanging: boolean;
private tokenLogin?: boolean; private tokenLogin?: boolean;
private accountPassword?: string; private accountPassword?: string;
@ -277,9 +277,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
} }
this.windowWidth = 10000; UIStore.instance.on(UI_EVENTS.Resize, this.handleResize);
this.handleResize();
window.addEventListener('resize', this.handleResize);
this.pageChanging = false; this.pageChanging = false;
@ -436,7 +434,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
this.themeWatcher.stop(); this.themeWatcher.stop();
this.fontWatcher.stop(); this.fontWatcher.stop();
window.removeEventListener('resize', this.handleResize); UIStore.destroy();
this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize); this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize);
if (this.accountPasswordTimer !== null) clearTimeout(this.accountPasswordTimer); if (this.accountPasswordTimer !== null) clearTimeout(this.accountPasswordTimer);
@ -1820,18 +1818,17 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
handleResize = () => { handleResize = () => {
const hideLhsThreshold = 1000; const LHS_THRESHOLD = 1000;
const showLhsThreshold = 1000; const width = UIStore.instance.windowWith;
if (this.windowWidth > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) { if (width <= LHS_THRESHOLD && !this.state.collapseLhs) {
dis.dispatch({ action: 'hide_left_panel' }); dis.dispatch({ action: 'hide_left_panel' });
} }
if (this.windowWidth <= showLhsThreshold && window.innerWidth > showLhsThreshold) { if (width > LHS_THRESHOLD && this.state.collapseLhs) {
dis.dispatch({ action: 'show_left_panel' }); dis.dispatch({ action: 'show_left_panel' });
} }
this.state.resizeNotifier.notifyWindowResized(); this.state.resizeNotifier.notifyWindowResized();
this.windowWidth = window.innerWidth;
}; };
private dispatchTimelineResize() { private dispatchTimelineResize() {

View file

@ -55,7 +55,6 @@ interface IProps {
onKeyDown: (ev: React.KeyboardEvent) => void; onKeyDown: (ev: React.KeyboardEvent) => void;
onFocus: (ev: React.FocusEvent) => void; onFocus: (ev: React.FocusEvent) => void;
onBlur: (ev: React.FocusEvent) => void; onBlur: (ev: React.FocusEvent) => void;
onResize: () => void;
resizeNotifier: ResizeNotifier; resizeNotifier: ResizeNotifier;
isMinimized: boolean; isMinimized: boolean;
activeSpace: Room; activeSpace: Room;
@ -404,9 +403,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
const newSublists = objectWithOnly(newLists, newListIds); const newSublists = objectWithOnly(newLists, newListIds);
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v)); const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
this.setState({sublists, isNameFiltering}, () => { this.setState({sublists, isNameFiltering});
this.props.onResize();
});
} }
}; };
@ -537,7 +534,6 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
addRoomLabel={aesthetics.addRoomLabel ? _t(aesthetics.addRoomLabel) : aesthetics.addRoomLabel} addRoomLabel={aesthetics.addRoomLabel ? _t(aesthetics.addRoomLabel) : aesthetics.addRoomLabel}
addRoomContextMenu={aesthetics.addRoomContextMenu} addRoomContextMenu={aesthetics.addRoomContextMenu}
isMinimized={this.props.isMinimized} isMinimized={this.props.isMinimized}
onResize={this.props.onResize}
showSkeleton={showSkeleton} showSkeleton={showSkeleton}
extraTiles={extraTiles} extraTiles={extraTiles}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}

View file

@ -74,7 +74,6 @@ interface IProps {
addRoomLabel: string; addRoomLabel: string;
isMinimized: boolean; isMinimized: boolean;
tagId: TagID; tagId: TagID;
onResize: () => void;
showSkeleton?: boolean; showSkeleton?: boolean;
alwaysVisible?: boolean; alwaysVisible?: boolean;
resizeNotifier: ResizeNotifier; resizeNotifier: ResizeNotifier;
@ -473,7 +472,6 @@ export default class RoomSublist extends React.Component<IProps, IState> {
private toggleCollapsed = () => { private toggleCollapsed = () => {
this.layout.isCollapsed = this.state.isExpanded; this.layout.isCollapsed = this.state.isExpanded;
this.setState({isExpanded: !this.layout.isCollapsed}); this.setState({isExpanded: !this.layout.isCollapsed});
setImmediate(() => this.props.onResize()); // needs to happen when the DOM is updated
}; };
private onHeaderKeyDown = (ev: React.KeyboardEvent) => { private onHeaderKeyDown = (ev: React.KeyboardEvent) => {

View file

@ -74,12 +74,6 @@ export default class ResizeNotifier extends EventEmitter {
// can be called in quick succession // can be called in quick succession
notifyWindowResized() { notifyWindowResized() {
// no need to throttle this one,
// also it could make scrollbars appear for
// a split second when the room list manual layout is now
// taller than the available space
this.emit("leftPanelResized");
this._updateMiddlePanel(); this._updateMiddlePanel();
} }
} }