diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js
index 5982c52d98..5efcefc4bc 100644
--- a/src/components/views/rooms/AppsDrawer.js
+++ b/src/components/views/rooms/AppsDrawer.js
@@ -28,12 +28,13 @@ import WidgetUtils from '../../../utils/WidgetUtils';
import WidgetEchoStore from "../../../stores/WidgetEchoStore";
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore";
-import {useLocalStorageState} from "../../../hooks/useLocalStorageState";
import ResizeNotifier from "../../../utils/ResizeNotifier";
import ResizeHandle from "../elements/ResizeHandle";
import Resizer from "../../../resizer/resizer";
import PercentageDistributor from "../../../resizer/distributors/percentage";
import {Container, WidgetLayoutStore} from "../../../stores/widgets/WidgetLayoutStore";
+import {clamp, percentageOf, percentageWithin} from "../../../utils/numbers";
+import {useStateCallback} from "../../../hooks/useStateCallback";
export default class AppsDrawer extends React.Component {
static propTypes = {
@@ -237,7 +238,7 @@ export default class AppsDrawer extends React.Component {
return (
{
- const [height, setHeight] = useLocalStorageState("pvr_" + id, 280); // old fixed height was 273px
+ let defaultHeight = WidgetLayoutStore.instance.getContainerHeight(room, Container.Top);
+
+ // Arbitrary defaults to avoid NaN problems. 100 px or 3/4 of the visible window.
+ if (!minHeight) minHeight = 100;
+ if (!maxHeight) maxHeight = (window.innerHeight / 4) * 3;
+
+ // Convert from percentage to height. Note that the default height is 280px.
+ if (defaultHeight) {
+ defaultHeight = clamp(defaultHeight, 0, 100);
+ defaultHeight = percentageWithin(defaultHeight / 100, minHeight, maxHeight);
+ } else {
+ defaultHeight = 280;
+ }
+
+ const [height, setHeight] = useStateCallback(defaultHeight, newHeight => {
+ newHeight = percentageOf(newHeight, minHeight, maxHeight) * 100;
+ WidgetLayoutStore.instance.setContainerHeight(room, Container.Top, newHeight)
+ });
return (initialValue: T, callback: (v: T) => void): [T, Dispatch>] => {
+ const [value, setValue] = useState(initialValue);
+ const interceptSetValue = (newVal: T) => {
+ setValue(newVal);
+ callback(newVal);
+ };
+ return [value, interceptSetValue];
+};
diff --git a/src/stores/widgets/WidgetLayoutStore.ts b/src/stores/widgets/WidgetLayoutStore.ts
index 7d22f6729e..12051d35bc 100644
--- a/src/stores/widgets/WidgetLayoutStore.ts
+++ b/src/stores/widgets/WidgetLayoutStore.ts
@@ -63,13 +63,15 @@ interface IStoredLayout {
// TODO: [Deferred] Maximizing (fullscreen) widgets by default.
}
+interface IWidgetLayouts {
+ [widgetId: string]: IStoredLayout;
+}
+
interface ILayoutStateEvent {
// TODO: [Deferred] Forced layout (fixed with no changes)
// The widget layouts.
- widgets: {
- [widgetId: string]: IStoredLayout;
- };
+ widgets: IWidgetLayouts;
}
interface ILayoutSettings extends ILayoutStateEvent {
@@ -79,8 +81,11 @@ interface ILayoutSettings extends ILayoutStateEvent {
// Dev note: "Pinned" widgets are ones in the top container.
const MAX_PINNED = 3;
-const MIN_WIDGET_WIDTH_PCT = 10; // Don't make anything smaller than 10% width
-const MIN_WIDGET_HEIGHT_PCT = 20;
+// These two are whole percentages and don't really mean anything. Later values will decide
+// minimum, but these help determine proportions during our calculations here. In fact, these
+// values should be *smaller* than the actual minimums imposed by later components.
+const MIN_WIDGET_WIDTH_PCT = 10; // 10%
+const MIN_WIDGET_HEIGHT_PCT = 2; // 2%
export class WidgetLayoutStore extends ReadyWatchingStore {
private static internalInstance: WidgetLayoutStore;
@@ -230,7 +235,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
// Determine width distribution and height of the top container now (the only relevant one)
const widths: number[] = [];
- let maxHeight = 0;
+ let maxHeight = null; // null == default
let doAutobalance = true;
for (let i = 0; i < topWidgets.length; i++) {
const widget = topWidgets[i];
@@ -246,9 +251,11 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
widths.push(100); // we'll figure this out later
}
- const defRoomHeight = defaultNumber(widgetLayout?.height, MIN_WIDGET_HEIGHT_PCT);
- const h = defaultNumber(userWidgetLayout?.height, defRoomHeight);
- maxHeight = Math.max(maxHeight, clamp(h, MIN_WIDGET_HEIGHT_PCT, 100));
+ if (widgetLayout?.height || userWidgetLayout?.height) {
+ const defRoomHeight = defaultNumber(widgetLayout?.height, MIN_WIDGET_HEIGHT_PCT);
+ const h = defaultNumber(userWidgetLayout?.height, defRoomHeight);
+ maxHeight = Math.max(maxHeight, clamp(h, MIN_WIDGET_HEIGHT_PCT, 100));
+ }
}
let remainingWidth = 100;
for (const width of widths) {
@@ -330,12 +337,35 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
localLayout[w.id] = {
width: numbers[i],
index: i,
+ height: this.byRoom[room.roomId]?.[container]?.height || MIN_WIDGET_HEIGHT_PCT,
};
});
+ this.updateUserLayout(room, localLayout);
+ }
+
+ public getContainerHeight(room: Room, container: Container): number {
+ return this.byRoom[room.roomId]?.[container]?.height; // let the default get returned if needed
+ }
+
+ public setContainerHeight(room: Room, container: Container, height: number) {
+ const widgets = this.getContainerWidgets(room, container);
+ const widths = this.byRoom[room.roomId]?.[container]?.distributions;
+ const localLayout = {};
+ widgets.forEach((w, i) => {
+ localLayout[w.id] = {
+ width: widths[i],
+ index: i,
+ height: height,
+ };
+ });
+ this.updateUserLayout(room, localLayout);
+ }
+
+ private updateUserLayout(room: Room, newLayout: IWidgetLayouts) {
const layoutEv = room.currentState.getStateEvents(WIDGET_LAYOUT_EVENT_TYPE, "");
SettingsStore.setValue("Widgets.layout", room.roomId, SettingLevel.ROOM_ACCOUNT, {
overrides: layoutEv?.getId(),
- widgets: localLayout,
+ widgets: newLayout,
});
}
}
diff --git a/src/utils/numbers.ts b/src/utils/numbers.ts
index e26db0d5aa..c8b9e7248f 100644
--- a/src/utils/numbers.ts
+++ b/src/utils/numbers.ts
@@ -32,3 +32,11 @@ export function clamp(i: number, min: number, max: number): number {
export function sum(...i: number[]): number {
return [...i].reduce((p, c) => c + p, 0);
}
+
+export function percentageWithin(pct: number, min: number, max: number): number {
+ return (pct * (max - min)) + min;
+}
+
+export function percentageOf(val: number, min: number, max: number): number {
+ return (val - min) / max;
+}