diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 689d02155f4..17748612800 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -52,8 +52,10 @@ import EditorStateTransfer from "../../utils/EditorStateTransfer"; import { Action } from '../../dispatcher/actions'; import { getEventDisplayInfo } from "../../utils/EventUtils"; import { IReadReceiptInfo } from "../views/rooms/ReadReceiptMarker"; +import UIStore, { UI_EVENTS } from '../../stores/UIStore'; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes +const NARROW_MODE_BREAKPOINT = 400; const continuedTypes = [EventType.Sticker, EventType.RoomMessage]; const groupedEvents = [ EventType.RoomMember, @@ -190,6 +192,7 @@ interface IState { ghostReadMarkers: string[]; showTypingNotifications: boolean; hideSender: boolean; + narrowMode?: boolean; } interface IReadReceiptForUser { @@ -255,6 +258,9 @@ export default class MessagePanel extends React.Component { private readonly showTypingNotificationsWatcherRef: string; private eventTiles: Record = {}; + private static instanceCount = 0; + private readonly instanceId: number; + constructor(props, context) { super(props, context); @@ -273,17 +279,24 @@ export default class MessagePanel extends React.Component { this.showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); + + this.instanceId = MessagePanel.instanceCount++; } componentDidMount() { this.calculateRoomMembersCount(); this.props.room?.on("RoomState.members", this.calculateRoomMembersCount); + UIStore.instance.on(`MessagePanel${this.instanceId}`, this.onResize); + UIStore.instance.trackElementDimensions(`MessagePanel${this.instanceId}`, + ReactDOM.findDOMNode(this.scrollPanel.current) as Element); this.isMounted = true; } componentWillUnmount() { this.isMounted = false; this.props.room?.off("RoomState.members", this.calculateRoomMembersCount); + UIStore.instance.off(`MessagePanel${this.instanceId}`, this.onResize); + UIStore.instance.stopTrackingElementDimensions(`MessagePanel${this.instanceId}`); SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef); } @@ -817,6 +830,7 @@ export default class MessagePanel extends React.Component { callEventGrouper={callEventGrouper} hideSender={this.state.hideSender} timelineRenderingType={this.context.timelineRenderingType} + narrowMode={this.state.narrowMode} /> , ); @@ -926,6 +940,13 @@ export default class MessagePanel extends React.Component { this.eventTiles[eventId] = node; }; + private onResize = (type: UI_EVENTS, entry: ResizeObserverEntry) => { + if (type !== UI_EVENTS.Resize) return; + this.setState({ + narrowMode: entry.contentRect.width <= NARROW_MODE_BREAKPOINT, + }); + }; + // once dynamic content in the events load, make the scrollPanel check the // scroll offsets. public onHeightChanged = (): void => { diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index f1f5a4f36db..fc86f376770 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -340,6 +340,9 @@ interface IProps { // displayed to the current user either because they're // the author or they are a moderator isSeeingThroughMessageHiddenForModeration?: boolean; + + // Whether we should assume a smaller width and adjust layout to match + narrowMode?: boolean; } interface IState { @@ -690,6 +693,12 @@ export default class EventTile extends React.Component {

{ _t("From a thread") }

); } else if (this.state.threadReplyCount && this.props.mxEvent.isThreadRoot) { + let count: string | number = this.state.threadReplyCount; + if (!this.props.narrowMode) { + count = _t("%(count)s reply", { + count: this.state.threadReplyCount, + }); + } return ( { context => @@ -700,9 +709,7 @@ export default class EventTile extends React.Component { }} > - { _t("%(count)s reply", { - count: this.state.threadReplyCount, - }) } + { count } { this.renderThreadLastMessagePreview() }