Refactoring readMarkerForEvent into ReadMarkerView in shared-components (#32777)
* Refactoring readMarkerForEvent into ReadMarketView in shared-components * Use shared ReadMarkerView in MessagePanel * Rename ReadMarkerView to ReadMarker * Fix Prettier * Update snapshots screenshots * Use plain props for ReadMarker * Fix Prettier * Move ReadMarker into room timeline * Replace ReadMarker nested ternary * Update snapshot
This commit is contained in:
@@ -68,7 +68,6 @@
|
||||
@import "./structures/_LeftPanel.pcss";
|
||||
@import "./structures/_MainSplit.pcss";
|
||||
@import "./structures/_MatrixChat.pcss";
|
||||
@import "./structures/_MessagePanel.pcss";
|
||||
@import "./structures/_NonUrgentToastContainer.pcss";
|
||||
@import "./structures/_PictureInPictureDragger.pcss";
|
||||
@import "./structures/_QuickSettingsButton.pcss";
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_MessagePanel_myReadMarker {
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
|
||||
hr {
|
||||
border-top: solid 1px $accent;
|
||||
border-bottom: solid 1px $accent;
|
||||
margin-top: 0;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
z-index: 1;
|
||||
will-change: width;
|
||||
transition:
|
||||
width 400ms easeinsine 1s,
|
||||
opacity 400ms easeinsine 1s;
|
||||
width: 99%;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, createRef, type ReactNode, type TransitionEvent } from "react";
|
||||
import React, { type JSX, createRef, type ReactNode, type TransitionEventHandler } from "react";
|
||||
import classNames from "classnames";
|
||||
import {
|
||||
type Room,
|
||||
@@ -20,6 +20,7 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { isSupportedReceiptType } from "matrix-js-sdk/src/utils";
|
||||
import {
|
||||
DateSeparatorView,
|
||||
ReadMarker,
|
||||
TimelineSeparator,
|
||||
useCreateAutoDisposedViewModel,
|
||||
} from "@element-hq/web-shared-components";
|
||||
@@ -271,7 +272,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||
private readonly _showHiddenEvents: boolean;
|
||||
private unmounted = false;
|
||||
|
||||
private readMarkerNode = createRef<HTMLLIElement>();
|
||||
private readMarkerNode: HTMLLIElement | null = null;
|
||||
private whoIsTyping = createRef<WhoIsTypingTile>();
|
||||
public scrollPanel = createRef<ScrollPanel>();
|
||||
|
||||
@@ -403,7 +404,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||
// 0: read marker is within the window
|
||||
// +1: read marker is below the window
|
||||
public getReadMarkerPosition(): number | null {
|
||||
const readMarker = this.readMarkerNode.current;
|
||||
const readMarker = this.readMarkerNode;
|
||||
const messageWrapper = this.scrollPanel.current?.divScroll;
|
||||
|
||||
if (!readMarker || !messageWrapper) {
|
||||
@@ -507,29 +508,18 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||
public readMarkerForEvent(eventId: string, isLastEvent: boolean): ReactNode {
|
||||
if (this.context.timelineRenderingType === TimelineRenderingType.File) return null;
|
||||
|
||||
const visible = !isLastEvent && this.props.readMarkerVisible;
|
||||
const showLine = !isLastEvent && !!this.props.readMarkerVisible;
|
||||
|
||||
if (this.props.readMarkerEventId === eventId) {
|
||||
let hr;
|
||||
// if the read marker comes at the end of the timeline (except
|
||||
// for local echoes, which are excluded from RMs, because they
|
||||
// don't have useful event ids), we don't want to show it, but
|
||||
// we still want to create the <li/> for it so that the
|
||||
// algorithms which depend on its position on the screen aren't
|
||||
// confused.
|
||||
if (visible) {
|
||||
hr = <hr style={{ opacity: 1, width: "99%" }} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
<ReadMarker
|
||||
key={"readMarker_" + eventId}
|
||||
ref={this.readMarkerNode}
|
||||
eventId={eventId}
|
||||
kind="current"
|
||||
showLine={showLine}
|
||||
onCurrentMarkerRef={this.collectReadMarker}
|
||||
className="mx_MessagePanel_myReadMarker"
|
||||
data-scroll-tokens={eventId}
|
||||
>
|
||||
{hr}
|
||||
</li>
|
||||
/>
|
||||
);
|
||||
} else if (this.state.ghostReadMarkers.includes(eventId)) {
|
||||
// We render 'ghost' read markers in the DOM while they
|
||||
@@ -542,28 +532,30 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||
// case is a little more complex because only some of the items
|
||||
// transition (ie. the read markers do but the event tiles do not)
|
||||
// and TransitionGroup requires that all its children are Transitions.
|
||||
const hr = (
|
||||
<hr
|
||||
ref={this.collectGhostReadMarker}
|
||||
onTransitionEnd={this.onGhostTransitionEnd}
|
||||
data-eventid={eventId}
|
||||
/>
|
||||
);
|
||||
|
||||
// give it a key which depends on the event id. That will ensure that
|
||||
// we get a new DOM node (restarting the animation) when the ghost
|
||||
// moves to a different event.
|
||||
return (
|
||||
<li key={"_readuptoghost_" + eventId} className="mx_MessagePanel_myReadMarker">
|
||||
{hr}
|
||||
</li>
|
||||
<ReadMarker
|
||||
key={"_readuptoghost_" + eventId}
|
||||
eventId={eventId}
|
||||
kind="ghost"
|
||||
showLine={true}
|
||||
onGhostLineRef={this.collectGhostReadMarker}
|
||||
onGhostTransitionEnd={this.onGhostTransitionEnd}
|
||||
className="mx_MessagePanel_myReadMarker"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private collectGhostReadMarker = (node: HTMLElement | null): void => {
|
||||
private collectReadMarker = (node: HTMLLIElement | null): void => {
|
||||
this.readMarkerNode = node;
|
||||
};
|
||||
|
||||
private collectGhostReadMarker = (node: HTMLHRElement | null): void => {
|
||||
if (node) {
|
||||
// now the element has appeared, change the style which will trigger the CSS transition
|
||||
requestAnimationFrame(() => {
|
||||
@@ -573,7 +565,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||
}
|
||||
};
|
||||
|
||||
private onGhostTransitionEnd = (ev: TransitionEvent): void => {
|
||||
private onGhostTransitionEnd: TransitionEventHandler<HTMLHRElement> = (ev): void => {
|
||||
// we can now clean up the ghost element
|
||||
const finishedEventId = (ev.target as HTMLElement).dataset.eventid;
|
||||
this.setState({
|
||||
|
||||
Reference in New Issue
Block a user