From bf8438cbb2897d3e33beb0435f6e93dbe6c4b15a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 8 Feb 2022 17:35:03 +0000 Subject: [PATCH] Add jump to bottom button for right panel timeline (#7746) --- res/css/views/right_panel/_TimelineCard.scss | 5 + .../views/right_panel/TimelineCard.tsx | 95 ++++++++++++++----- .../views/rooms/JumpToBottomButton.tsx | 2 +- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/res/css/views/right_panel/_TimelineCard.scss b/res/css/views/right_panel/_TimelineCard.scss index ab48567907..5acdc1a8e9 100644 --- a/res/css/views/right_panel/_TimelineCard.scss +++ b/res/css/views/right_panel/_TimelineCard.scss @@ -112,3 +112,8 @@ limitations under the License. flex-basis: 48px; // 12 (padding on message list) + 36 (padding on event lines) } } + +.mx_TimelineCard_timeline { + overflow: hidden; + position: relative; // offset parent for jump to bottom button +} diff --git a/src/components/views/right_panel/TimelineCard.tsx b/src/components/views/right_panel/TimelineCard.tsx index f8fc84b676..328f2baf59 100644 --- a/src/components/views/right_panel/TimelineCard.tsx +++ b/src/components/views/right_panel/TimelineCard.tsx @@ -16,7 +16,9 @@ limitations under the License. import React from 'react'; import { EventSubscription } from "fbemitter"; -import { EventTimelineSet, IEventRelation, MatrixEvent, Room } from 'matrix-js-sdk/src'; +import { IEventRelation, MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set'; +import { NotificationCountType, Room } from 'matrix-js-sdk/src/models/room'; import { Thread } from 'matrix-js-sdk/src/models/thread'; import classNames from 'classnames'; @@ -38,6 +40,7 @@ import RoomViewStore from '../../../stores/RoomViewStore'; import ContentMessages from '../../../ContentMessages'; import UploadBar from '../../structures/UploadBar'; import SettingsStore from '../../../settings/SettingsStore'; +import JumpToBottomButton from '../rooms/JumpToBottomButton'; interface IProps { room: Room; @@ -58,6 +61,7 @@ interface IState { initialEventId?: string; isInitialEventHighlighted?: boolean; layout: Layout; + atEndOfLiveTimeline: boolean; // settings: showReadReceipts?: boolean; @@ -78,6 +82,7 @@ export default class TimelineCard extends React.Component { this.state = { showReadReceipts: SettingsStore.getValue("showReadReceipts", props.room.roomId), layout: SettingsStore.getValue("layout"), + atEndOfLiveTimeline: true, }; this.readReceiptsSettingWatcher = null; } @@ -137,7 +142,7 @@ export default class TimelineCard extends React.Component { } }; - private onScroll = (): void => { + private onUserScroll = (): void => { if (this.state.initialEventId && this.state.isInitialEventHighlighted) { dis.dispatch({ action: Action.ViewRoom, @@ -149,6 +154,36 @@ export default class TimelineCard extends React.Component { } }; + private onScroll = (): void => { + const timelinePanel = this.timelinePanelRef.current; + if (!timelinePanel) return; + if (timelinePanel.isAtEndOfLiveTimeline()) { + this.setState({ + atEndOfLiveTimeline: true, + }); + } else { + this.setState({ + atEndOfLiveTimeline: false, + }); + } + }; + + private jumpToLiveTimeline = () => { + if (this.state.initialEventId && this.state.isInitialEventHighlighted) { + // If we were viewing a highlighted event, firing view_room without + // an event will take care of both clearing the URL fragment and + // jumping to the bottom + dis.dispatch({ + action: Action.ViewRoom, + room_id: this.props.room.roomId, + }); + } else { + // Otherwise we have to jump manually + this.timelinePanelRef.current?.jumpToLiveTimeline(); + dis.fire(Action.FocusSendMessageComposer); + } + }; + private renderTimelineCardHeader = (): JSX.Element => { return
{ _t("Chat") } @@ -165,6 +200,14 @@ export default class TimelineCard extends React.Component { "mx_GroupLayout": this.state.layout === Layout.Group, }); + let jumpToBottom; + if (!this.state.atEndOfLiveTimeline) { + jumpToBottom = ( 0} + onScrollToBottomClick={this.jumpToLiveTimeline} + />); + } + return ( { withoutScrollContainer={true} header={this.renderTimelineCardHeader()} > -