Migrat ReadReceiptMarker to TypeScript
This commit is contained in:
parent
2e9cacdeb1
commit
e9e6269da7
2 changed files with 64 additions and 51 deletions
|
@ -240,6 +240,7 @@ interface IProps {
|
||||||
// opaque readreceipt info for each userId; used by ReadReceiptMarker
|
// opaque readreceipt info for each userId; used by ReadReceiptMarker
|
||||||
// to manage its animations. Should be an empty object when the room
|
// to manage its animations. Should be an empty object when the room
|
||||||
// first loads
|
// first loads
|
||||||
|
// TODO: Proper typing for RR info
|
||||||
readReceiptMap?: any;
|
readReceiptMap?: any;
|
||||||
|
|
||||||
// A function which is used to check if the parent panel is being
|
// A function which is used to check if the parent panel is being
|
||||||
|
|
|
@ -16,7 +16,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { createRef } from 'react';
|
import React, { createRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { RoomMember } from 'matrix-js-sdk/src';
|
||||||
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { formatDate } from '../../../DateUtils';
|
import { formatDate } from '../../../DateUtils';
|
||||||
import NodeAnimator from "../../../NodeAnimator";
|
import NodeAnimator from "../../../NodeAnimator";
|
||||||
|
@ -24,53 +25,64 @@ import * as sdk from "../../../index";
|
||||||
import { toPx } from "../../../utils/units";
|
import { toPx } from "../../../utils/units";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
// the RoomMember to show the RR for
|
||||||
|
member?: RoomMember;
|
||||||
|
// userId to fallback the avatar to
|
||||||
|
// if the member hasn't been loaded yet
|
||||||
|
fallbackUserId: string;
|
||||||
|
|
||||||
|
// number of pixels to offset the avatar from the right of its parent;
|
||||||
|
// typically a negative value.
|
||||||
|
leftOffset?: number;
|
||||||
|
|
||||||
|
// true to hide the avatar (it will still be animated)
|
||||||
|
hidden?: boolean;
|
||||||
|
|
||||||
|
// don't animate this RR into position
|
||||||
|
suppressAnimation?: boolean;
|
||||||
|
|
||||||
|
// an opaque object for storing information about this user's RR in
|
||||||
|
// this room
|
||||||
|
// TODO: proper typing for RR info
|
||||||
|
readReceiptInfo: any;
|
||||||
|
|
||||||
|
// A function which is used to check if the parent panel is being
|
||||||
|
// unmounted, to avoid unnecessary work. Should return true if we
|
||||||
|
// are being unmounted.
|
||||||
|
checkUnmounting?: () => boolean;
|
||||||
|
|
||||||
|
// callback for clicks on this RR
|
||||||
|
onClick?: (e: React.MouseEvent) => void;
|
||||||
|
|
||||||
|
// Timestamp when the receipt was read
|
||||||
|
timestamp?: number;
|
||||||
|
|
||||||
|
// True to show twelve hour format, false otherwise
|
||||||
|
showTwelveHour?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
suppressDisplay: boolean;
|
||||||
|
startStyles?: IReadReceiptMarkerStyle[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IReadReceiptMarkerStyle {
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.ReadReceiptMarker")
|
@replaceableComponent("views.rooms.ReadReceiptMarker")
|
||||||
export default class ReadReceiptMarker extends React.PureComponent {
|
export default class ReadReceiptMarker extends React.PureComponent<IProps, IState> {
|
||||||
static propTypes = {
|
private avatar: React.RefObject<HTMLDivElement> = createRef();
|
||||||
// the RoomMember to show the RR for
|
|
||||||
member: PropTypes.object,
|
|
||||||
// userId to fallback the avatar to
|
|
||||||
// if the member hasn't been loaded yet
|
|
||||||
fallbackUserId: PropTypes.string.isRequired,
|
|
||||||
|
|
||||||
// number of pixels to offset the avatar from the right of its parent;
|
|
||||||
// typically a negative value.
|
|
||||||
leftOffset: PropTypes.number,
|
|
||||||
|
|
||||||
// true to hide the avatar (it will still be animated)
|
|
||||||
hidden: PropTypes.bool,
|
|
||||||
|
|
||||||
// don't animate this RR into position
|
|
||||||
suppressAnimation: PropTypes.bool,
|
|
||||||
|
|
||||||
// an opaque object for storing information about this user's RR in
|
|
||||||
// this room
|
|
||||||
readReceiptInfo: PropTypes.object,
|
|
||||||
|
|
||||||
// A function which is used to check if the parent panel is being
|
|
||||||
// unmounted, to avoid unnecessary work. Should return true if we
|
|
||||||
// are being unmounted.
|
|
||||||
checkUnmounting: PropTypes.func,
|
|
||||||
|
|
||||||
// callback for clicks on this RR
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
|
|
||||||
// Timestamp when the receipt was read
|
|
||||||
timestamp: PropTypes.number,
|
|
||||||
|
|
||||||
// True to show twelve hour format, false otherwise
|
|
||||||
showTwelveHour: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
leftOffset: 0,
|
leftOffset: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._avatar = createRef();
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
// if we are going to animate the RR, we don't show it on first render,
|
// if we are going to animate the RR, we don't show it on first render,
|
||||||
// and instead just add a placeholder to the DOM; once we've been
|
// and instead just add a placeholder to the DOM; once we've been
|
||||||
|
@ -80,7 +92,7 @@ export default class ReadReceiptMarker extends React.PureComponent {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
// before we remove the rr, store its location in the map, so that if
|
// before we remove the rr, store its location in the map, so that if
|
||||||
// it reappears, it can be animated from the right place.
|
// it reappears, it can be animated from the right place.
|
||||||
const rrInfo = this.props.readReceiptInfo;
|
const rrInfo = this.props.readReceiptInfo;
|
||||||
|
@ -95,29 +107,29 @@ export default class ReadReceiptMarker extends React.PureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatarNode = this._avatar.current;
|
const avatarNode = this.avatar.current;
|
||||||
rrInfo.top = avatarNode.offsetTop;
|
rrInfo.top = avatarNode.offsetTop;
|
||||||
rrInfo.left = avatarNode.offsetLeft;
|
rrInfo.left = avatarNode.offsetLeft;
|
||||||
rrInfo.parent = avatarNode.offsetParent;
|
rrInfo.parent = avatarNode.offsetParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount(): void {
|
||||||
if (!this.state.suppressDisplay) {
|
if (!this.state.suppressDisplay) {
|
||||||
// we've already done our display - nothing more to do.
|
// we've already done our display - nothing more to do.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._animateMarker();
|
this.animateMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
public componentDidUpdate(prevProps: IProps): void {
|
||||||
const differentLeftOffset = prevProps.leftOffset !== this.props.leftOffset;
|
const differentLeftOffset = prevProps.leftOffset !== this.props.leftOffset;
|
||||||
const visibilityChanged = prevProps.hidden !== this.props.hidden;
|
const visibilityChanged = prevProps.hidden !== this.props.hidden;
|
||||||
if (differentLeftOffset || visibilityChanged) {
|
if (differentLeftOffset || visibilityChanged) {
|
||||||
this._animateMarker();
|
this.animateMarker();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_animateMarker() {
|
private animateMarker(): void {
|
||||||
// treat new RRs as though they were off the top of the screen
|
// treat new RRs as though they were off the top of the screen
|
||||||
let oldTop = -15;
|
let oldTop = -15;
|
||||||
|
|
||||||
|
@ -126,7 +138,7 @@ export default class ReadReceiptMarker extends React.PureComponent {
|
||||||
oldTop = oldInfo.top + oldInfo.parent.getBoundingClientRect().top;
|
oldTop = oldInfo.top + oldInfo.parent.getBoundingClientRect().top;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newElement = this._avatar.current;
|
const newElement = this.avatar.current;
|
||||||
let startTopOffset;
|
let startTopOffset;
|
||||||
if (!newElement.offsetParent) {
|
if (!newElement.offsetParent) {
|
||||||
// this seems to happen sometimes for reasons I don't understand
|
// this seems to happen sometimes for reasons I don't understand
|
||||||
|
@ -156,10 +168,10 @@ export default class ReadReceiptMarker extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
public render(): JSX.Element {
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
if (this.state.suppressDisplay) {
|
if (this.state.suppressDisplay) {
|
||||||
return <div ref={this._avatar} />;
|
return <div ref={this.avatar} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
|
@ -198,7 +210,7 @@ export default class ReadReceiptMarker extends React.PureComponent {
|
||||||
style={style}
|
style={style}
|
||||||
title={title}
|
title={title}
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
inputRef={this._avatar}
|
inputRef={this.avatar}
|
||||||
/>
|
/>
|
||||||
</NodeAnimator>
|
</NodeAnimator>
|
||||||
);
|
);
|
Loading…
Reference in a new issue