FIX: ensures chat panel can't have an invalid width (#27876)

Prior to this fix the following sequence would cause an overflow:

- open a thread
- expand thread panel to maximum width
- close panel
- reduce window width
- open thread again
- 💥

The fix is now ensuring that we never use or set a width which would cause the main panel + side panel to be larger than the chat container. We also removed the service as it was overkill for this case and it's easier to have all the implementation at one place.

This commit also uses JS animation api to set the width of the panel.

<!-- NOTE: All pull requests should have tests (rspec in Ruby, qunit in JavaScript). If your code does not include test coverage, please include an explanation of why it was omitted. -->
This commit is contained in:
Joffrey JAFFEUX 2024-07-11 20:27:30 +02:00 committed by GitHub
parent 88c2b1c01b
commit 897518e874
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 44 additions and 52 deletions

View File

@ -1,47 +1,65 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import { and } from "truth-helpers";
import KeyValueStore from "discourse/lib/key-value-store";
import resizableNode from "../modifiers/chat/resizable-node";
import ChatSidePanelResizer from "./chat-side-panel-resizer";
const MIN_CHAT_CHANNEL_WIDTH = 250;
const MIN_PANEL_WIDTH = 250;
const STORE_NAMESPACE = "discourse_chat_side_panel_size_";
export default class ChatSidePanel extends Component {
@service chatStateManager;
@service chatSidePanelSize;
@service site;
@tracked widthStyle;
store = new KeyValueStore(STORE_NAMESPACE);
@action
setupSize() {
this.widthStyle = htmlSafe(`width:${this.chatSidePanelSize.width}px`);
setupSize(element) {
if (this.site.mobileView) {
return;
}
if (!this.chatStateManager.isFullPageActive) {
return;
}
const validWidth = Math.min(
this.store.getObject("width"),
this.mainContainerWidth - MIN_PANEL_WIDTH
);
element.animate(
[{ width: element.style.width }, { width: validWidth + "px" }],
{ duration: 0, fill: "forwards" }
);
this.storeWidth(validWidth);
}
@action
didResize(element, size) {
if (this.isDestroying || this.isDestroyed) {
return;
}
const mainPanelWidth = this.mainContainerWidth - size.width;
const parentWidth = element.parentElement.getBoundingClientRect().width;
const mainPanelWidth = parentWidth - size.width;
if (mainPanelWidth >= MIN_CHAT_CHANNEL_WIDTH) {
this.chatSidePanelSize.width = size.width;
element.style.width = size.width + "px";
this.widthStyle = htmlSafe(`width:${size.width}px`);
if (size.width >= MIN_PANEL_WIDTH && mainPanelWidth >= MIN_PANEL_WIDTH) {
element.animate(
[{ width: element.style.width }, { width: size.width + "px" }],
{ duration: 0, fill: "forwards" }
);
this.storeWidth(size.width);
}
}
#maxWidth(element) {
const parentWidth = element.parentElement.getBoundingClientRect().width;
return parentWidth - MIN_CHAT_CHANNEL_WIDTH;
get mainContainerWidth() {
return document.getElementById("main-chat-outlet").clientWidth;
}
storeWidth(width) {
this.store.setObject({
key: "width",
value: width,
});
}
<template>
@ -56,10 +74,6 @@ export default class ChatSidePanel extends Component {
position=false vertical=false mutate=false resetOnWindowResize=true
)
}}
style={{if
(and this.site.desktopView this.chatStateManager.isFullPageActive)
this.widthStyle
}}
>
{{yield}}

View File

@ -1,7 +1,7 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
import SidePanel from "discourse/plugins/chat/discourse/components/chat-side-panel";
import ChatSidePanel from "discourse/plugins/chat/discourse/components/chat-side-panel";
import FullPageChat from "discourse/plugins/chat/discourse/components/full-page-chat";
export default class ChatRoutesChannel extends Component {
@ -32,8 +32,8 @@ export default class ChatRoutesChannel extends Component {
/>
</div>
<SidePanel>
<ChatSidePanel>
{{outlet}}
</SidePanel>
</ChatSidePanel>
</template>
}

View File

@ -1,24 +0,0 @@
import Service from "@ember/service";
import KeyValueStore from "discourse/lib/key-value-store";
export default class ChatSidePanelSize extends Service {
STORE_NAMESPACE = "discourse_chat_side_panel_size_";
MIN_WIDTH = 250;
store = new KeyValueStore(this.STORE_NAMESPACE);
get width() {
return this.store.getObject("width") || this.MIN_WIDTH;
}
set width(width) {
this.store.setObject({
key: "width",
value: this.#min(width, this.MIN_WIDTH),
});
}
#min(number, min) {
return Math.max(number, min);
}
}

View File

@ -267,6 +267,7 @@ html.has-full-page-chat {
.main-chat-outlet {
min-height: 0;
max-width: 100vw;
box-sizing: border-box;
}
}
}

View File

@ -24,6 +24,7 @@
margin-left: auto;
margin-right: auto;
gap: 0.25rem;
box-sizing: border-box;
}
.dismiss-btn {