Simplify EventTile structure

Only render MessageTimestamp to the DOM when a tile is hovered
This commit is contained in:
Germain Souquet 2021-05-20 15:26:02 +01:00
parent d362321706
commit 171539d42d
2 changed files with 61 additions and 50 deletions

View file

@ -645,39 +645,36 @@ export default class MessagePanel extends React.Component {
// use txnId as key if available so that we don't remount during sending // use txnId as key if available so that we don't remount during sending
ret.push( ret.push(
<li <TileErrorBoundary key={mxEv.getTxnId() || eventId} mxEvent={mxEv}>
key={mxEv.getTxnId() || eventId} <EventTile
ref={this._collectEventNode.bind(this, eventId)} as="li"
data-scroll-tokens={scrollToken} data-scroll-tokens={scrollToken}
> ref={this._collectEventNode.bind(this, eventId)}
<TileErrorBoundary mxEvent={mxEv}> mxEvent={mxEv}
<EventTile continuation={continuation}
mxEvent={mxEv} isRedacted={mxEv.isRedacted()}
continuation={continuation} replacingEventId={mxEv.replacingEventId()}
isRedacted={mxEv.isRedacted()} editState={isEditing && this.props.editState}
replacingEventId={mxEv.replacingEventId()} onHeightChanged={this._onHeightChanged}
editState={isEditing && this.props.editState} readReceipts={readReceipts}
onHeightChanged={this._onHeightChanged} readReceiptMap={this._readReceiptMap}
readReceipts={readReceipts} showUrlPreview={this.props.showUrlPreview}
readReceiptMap={this._readReceiptMap} checkUnmounting={this._isUnmounting}
showUrlPreview={this.props.showUrlPreview} eventSendStatus={mxEv.getAssociatedStatus()}
checkUnmounting={this._isUnmounting} tileShape={this.props.tileShape}
eventSendStatus={mxEv.getAssociatedStatus()} isTwelveHour={this.props.isTwelveHour}
tileShape={this.props.tileShape} permalinkCreator={this.props.permalinkCreator}
isTwelveHour={this.props.isTwelveHour} last={last}
permalinkCreator={this.props.permalinkCreator} lastInSection={willWantDateSeparator}
last={last} lastSuccessful={isLastSuccessful}
lastInSection={willWantDateSeparator} isSelectedEvent={highlight}
lastSuccessful={isLastSuccessful} getRelationsForEvent={this.props.getRelationsForEvent}
isSelectedEvent={highlight} showReactions={this.props.showReactions}
getRelationsForEvent={this.props.getRelationsForEvent} layout={this.props.layout}
showReactions={this.props.showReactions} enableFlair={this.props.enableFlair}
layout={this.props.layout} showReadReceipts={this.props.showReadReceipts}
enableFlair={this.props.enableFlair} />
showReadReceipts={this.props.showReadReceipts} </TileErrorBoundary>,
/>
</TileErrorBoundary>
</li>,
); );
return ret; return ret;
@ -779,7 +776,7 @@ export default class MessagePanel extends React.Component {
} }
_collectEventNode = (eventId, node) => { _collectEventNode = (eventId, node) => {
this.eventNodes[eventId] = node; this.eventNodes[eventId] = node?.ref?.current;
} }
// once dynamic content in the events load, make the scrollPanel check the // once dynamic content in the events load, make the scrollPanel check the

View file

@ -277,6 +277,9 @@ interface IProps {
// Helper to build permalinks for the room // Helper to build permalinks for the room
permalinkCreator?: RoomPermalinkCreator; permalinkCreator?: RoomPermalinkCreator;
// Symbol of the root node
as?: string
} }
interface IState { interface IState {
@ -291,6 +294,8 @@ interface IState {
previouslyRequestedKeys: boolean; previouslyRequestedKeys: boolean;
// The Relations model from the JS SDK for reactions to `mxEvent` // The Relations model from the JS SDK for reactions to `mxEvent`
reactions: Relations; reactions: Relations;
hover: boolean;
} }
@replaceableComponent("views.rooms.EventTile") @replaceableComponent("views.rooms.EventTile")
@ -322,6 +327,8 @@ export default class EventTile extends React.Component<IProps, IState> {
previouslyRequestedKeys: false, previouslyRequestedKeys: false,
// The Relations model from the JS SDK for reactions to `mxEvent` // The Relations model from the JS SDK for reactions to `mxEvent`
reactions: this.getReactions(), reactions: this.getReactions(),
hover: false,
}; };
// don't do RR animations until we are mounted // don't do RR animations until we are mounted
@ -333,6 +340,8 @@ export default class EventTile extends React.Component<IProps, IState> {
// to determine if we've already subscribed and use a combination of other flags to find // to determine if we've already subscribed and use a combination of other flags to find
// out if we should even be subscribed at all. // out if we should even be subscribed at all.
this.isListeningForReceipts = false; this.isListeningForReceipts = false;
this.ref = React.createRef();
} }
/** /**
@ -960,7 +969,7 @@ export default class EventTile extends React.Component<IProps, IState> {
onFocusChange={this.onActionBarFocusChange} onFocusChange={this.onActionBarFocusChange}
/> : undefined; /> : undefined;
const timestamp = this.props.mxEvent.getTs() ? const timestamp = this.props.mxEvent.getTs() && this.state.hover ?
<MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null; <MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null;
const keyRequestHelpText = const keyRequestHelpText =
@ -1131,11 +1140,20 @@ export default class EventTile extends React.Component<IProps, IState> {
// tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers
return ( return (
<div className={classes} tabIndex={-1} aria-live={ariaLive} aria-atomic="true"> React.createElement(this.props.as || "div", {
{ ircTimestamp } "ref": this.ref,
{ sender } "className": classes,
{ ircPadlock } "tabIndex": -1,
<div className="mx_EventTile_line"> "aria-live": ariaLive,
"aria-atomic": "true",
"data-scroll-tokens": this.props["data-scroll-tokens"],
"onMouseEnter": () => this.setState({ hover: true }),
"onMouseLeave": () => this.setState({ hover: false }),
}, [
ircTimestamp,
sender,
ircPadlock,
<div className="mx_EventTile_line" key="mx_EventTile_line">
{ groupTimestamp } { groupTimestamp }
{ groupPadlock } { groupPadlock }
{ thread } { thread }
@ -1152,16 +1170,12 @@ export default class EventTile extends React.Component<IProps, IState> {
{ keyRequestInfo } { keyRequestInfo }
{ reactionsRow } { reactionsRow }
{ actionBar } { actionBar }
</div> </div>,
{msgOption} msgOption,
{ avatar,
// The avatar goes after the event tile as it's absolutely positioned to be over the
// event tile line, so needs to be later in the DOM so it appears on top (this avoids ])
// the need for further z-indexing chaos) )
}
{ avatar }
</div>
);
} }
} }
} }