|
|
@ -32,16 +32,16 @@ class Status extends ImmutablePureComponent { |
|
|
|
onOpenMedia: PropTypes.func, |
|
|
|
onOpenVideo: PropTypes.func, |
|
|
|
onBlock: PropTypes.func, |
|
|
|
onRef: PropTypes.func, |
|
|
|
isIntersecting: PropTypes.bool, |
|
|
|
me: PropTypes.number, |
|
|
|
boostModal: PropTypes.bool, |
|
|
|
autoPlayGif: PropTypes.bool, |
|
|
|
muted: PropTypes.bool, |
|
|
|
intersectionObserverWrapper: PropTypes.object, |
|
|
|
}; |
|
|
|
|
|
|
|
state = { |
|
|
|
isHidden: false, |
|
|
|
isIntersecting: true, // assume intersecting until told otherwise
|
|
|
|
isHidden: false, // set to true in requestIdleCallback to trigger un-render
|
|
|
|
} |
|
|
|
|
|
|
|
// Avoid checking props that are functions (and whose equality will always
|
|
|
@ -59,12 +59,12 @@ class Status extends ImmutablePureComponent { |
|
|
|
updateOnStates = [] |
|
|
|
|
|
|
|
shouldComponentUpdate (nextProps, nextState) { |
|
|
|
if (nextProps.isIntersecting === false && nextState.isHidden) { |
|
|
|
if (!nextState.isIntersecting && nextState.isHidden) { |
|
|
|
// It's only if we're not intersecting (i.e. offscreen) and isHidden is true
|
|
|
|
// that either "isIntersecting" or "isHidden" matter, and then they're
|
|
|
|
// the only things that matter.
|
|
|
|
return this.props.isIntersecting !== false || !this.state.isHidden; |
|
|
|
} else if (nextProps.isIntersecting !== false && this.props.isIntersecting === false) { |
|
|
|
return this.state.isIntersecting || !this.state.isHidden; |
|
|
|
} else if (nextState.isIntersecting && !this.state.isIntersecting) { |
|
|
|
// If we're going from a non-intersecting state to an intersecting state,
|
|
|
|
// (i.e. offscreen to onscreen), then we definitely need to re-render
|
|
|
|
return true; |
|
|
@ -73,21 +73,47 @@ class Status extends ImmutablePureComponent { |
|
|
|
return super.shouldComponentUpdate(nextProps, nextState); |
|
|
|
} |
|
|
|
|
|
|
|
componentWillReceiveProps (nextProps) { |
|
|
|
if (nextProps.isIntersecting === false && this.props.isIntersecting !== false) { |
|
|
|
requestIdleCallback(() => this.setState({ isHidden: true })); |
|
|
|
} else { |
|
|
|
this.setState({ isHidden: !nextProps.isIntersecting }); |
|
|
|
componentDidMount () { |
|
|
|
if (!this.props.intersectionObserverWrapper) { |
|
|
|
// TODO: enable IntersectionObserver optimization for notification statuses.
|
|
|
|
// These are managed in notifications/index.js rather than status_list.js
|
|
|
|
return; |
|
|
|
} |
|
|
|
this.props.intersectionObserverWrapper.observe( |
|
|
|
this.props.id, |
|
|
|
this.node, |
|
|
|
this.handleIntersection |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
handleRef = (node) => { |
|
|
|
if (this.props.onRef) { |
|
|
|
this.props.onRef(node); |
|
|
|
|
|
|
|
if (node && node.children.length !== 0) { |
|
|
|
this.height = node.clientHeight; |
|
|
|
handleIntersection = (entry) => { |
|
|
|
// Edge 15 doesn't support isIntersecting, but we can infer it from intersectionRatio
|
|
|
|
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12156111/
|
|
|
|
const isIntersecting = entry.intersectionRatio > 0; |
|
|
|
this.setState((prevState) => { |
|
|
|
if (prevState.isIntersecting && !isIntersecting) { |
|
|
|
requestIdleCallback(this.hideIfNotIntersecting); |
|
|
|
} |
|
|
|
return { |
|
|
|
isIntersecting: isIntersecting, |
|
|
|
isHidden: false, |
|
|
|
}; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
hideIfNotIntersecting = () => { |
|
|
|
// When the browser gets a chance, test if we're still not intersecting,
|
|
|
|
// and if so, set our isHidden to true to trigger an unrender. The point of
|
|
|
|
// this is to save DOM nodes and avoid using up too much memory.
|
|
|
|
// See: https://github.com/tootsuite/mastodon/issues/2900
|
|
|
|
this.setState((prevState) => ({ isHidden: !prevState.isIntersecting })); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
handleRef = (node) => { |
|
|
|
this.node = node; |
|
|
|
if (node && node.children.length !== 0) { |
|
|
|
this.height = node.clientHeight; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -107,14 +133,14 @@ class Status extends ImmutablePureComponent { |
|
|
|
render () { |
|
|
|
let media = null; |
|
|
|
let statusAvatar; |
|
|
|
const { status, account, isIntersecting, onRef, ...other } = this.props; |
|
|
|
const { isHidden } = this.state; |
|
|
|
const { status, account, ...other } = this.props; |
|
|
|
const { isIntersecting, isHidden } = this.state; |
|
|
|
|
|
|
|
if (status === null) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
if (isIntersecting === false && isHidden) { |
|
|
|
if (!isIntersecting && isHidden) { |
|
|
|
return ( |
|
|
|
<div ref={this.handleRef} data-id={status.get('id')} style={{ height: `${this.height}px`, opacity: 0, overflow: 'hidden' }}> |
|
|
|
{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} |
|
|
|