Implement new Read Receipt design (#8389)

* feat: introduce new alignment types for tooltip
* feat: introduce new hook for tooltips
* feat: allow using onFocus callback for RovingAccessibleButton
* feat: allow using custom class for ContextMenu
* feat: allow setting tab index for avatar
* refactor: move read receipts out of event tile
* feat: implement new read receipt design
* feat: update SentReceipt to match new read receipts as well
This commit is contained in:
Janne Mareike Koschinski 2022-04-22 17:09:44 +02:00 committed by GitHub
parent 03c46770f4
commit ee2ee3c08c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 553 additions and 270 deletions

View file

@ -19,15 +19,13 @@ import React, { createRef, RefObject } from 'react';
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from '../../../languageHandler';
import { formatDate } from '../../../DateUtils';
import NodeAnimator from "../../../NodeAnimator";
import { toPx } from "../../../utils/units";
import MemberAvatar from '../avatars/MemberAvatar';
export interface IReadReceiptInfo {
top?: number;
left?: number;
right?: number;
parent?: Element;
}
@ -40,7 +38,7 @@ interface IProps {
// number of pixels to offset the avatar from the right of its parent;
// typically a negative value.
leftOffset?: number;
offset: number;
// true to hide the avatar (it will still be animated)
hidden?: boolean;
@ -56,9 +54,6 @@ interface IProps {
// are being unmounted.
checkUnmounting?: () => boolean;
// callback for clicks on this RR
onClick?: (e: React.MouseEvent) => void;
// Timestamp when the receipt was read
timestamp?: number;
@ -73,16 +68,12 @@ interface IState {
interface IReadReceiptMarkerStyle {
top: number;
left: number;
right: number;
}
export default class ReadReceiptMarker extends React.PureComponent<IProps, IState> {
private avatar: React.RefObject<HTMLDivElement | HTMLImageElement | HTMLSpanElement> = createRef();
static defaultProps = {
leftOffset: 0,
};
constructor(props: IProps) {
super(props);
@ -112,7 +103,7 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
const avatarNode = this.avatar.current;
rrInfo.top = avatarNode.offsetTop;
rrInfo.left = avatarNode.offsetLeft;
rrInfo.right = avatarNode.getBoundingClientRect().right - avatarNode.offsetParent.getBoundingClientRect().right;
rrInfo.parent = avatarNode.offsetParent;
}
@ -125,9 +116,9 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
}
public componentDidUpdate(prevProps: IProps): void {
const differentLeftOffset = prevProps.leftOffset !== this.props.leftOffset;
const differentOffset = prevProps.offset !== this.props.offset;
const visibilityChanged = prevProps.hidden !== this.props.hidden;
if (differentLeftOffset || visibilityChanged) {
if (differentOffset || visibilityChanged) {
this.animateMarker();
}
}
@ -157,13 +148,13 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
const startStyles = [];
if (oldInfo && oldInfo.left) {
if (oldInfo && oldInfo.right) {
// start at the old height and in the old h pos
startStyles.push({ top: startTopOffset+"px",
left: toPx(oldInfo.left) });
right: toPx(oldInfo.right) });
}
startStyles.push({ top: startTopOffset+'px', left: '0' });
startStyles.push({ top: startTopOffset+'px', right: '0' });
this.setState({
suppressDisplay: false,
@ -177,29 +168,10 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
}
const style = {
left: toPx(this.props.leftOffset),
right: toPx(this.props.offset),
top: '0px',
};
let title;
if (this.props.timestamp) {
const dateString = formatDate(new Date(this.props.timestamp), this.props.showTwelveHour);
if (!this.props.member || this.props.fallbackUserId === this.props.member.rawDisplayName) {
title = _t(
"Seen by %(userName)s at %(dateTime)s",
{ userName: this.props.fallbackUserId,
dateTime: dateString },
);
} else {
title = _t(
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s",
{ displayName: this.props.member.rawDisplayName,
userName: this.props.fallbackUserId,
dateTime: dateString },
);
}
}
return (
<NodeAnimator startStyles={this.state.startStyles}>
<MemberAvatar
@ -211,9 +183,9 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
height={14}
resizeMethod="crop"
style={style}
title={title}
onClick={this.props.onClick}
inputRef={this.avatar as RefObject<HTMLImageElement>}
hideTitle
tabIndex={-1}
/>
</NodeAnimator>
);