mirror of
https://github.com/mastodon/mastodon.git
synced 2025-11-28 18:40:47 +00:00
Fix scroll-to-status in threaded view being unreliable
This commit is contained in:
parent
b14f113929
commit
bc98e835a4
|
|
@ -4,7 +4,7 @@
|
||||||
@typescript-eslint/no-unsafe-assignment */
|
@typescript-eslint/no-unsafe-assignment */
|
||||||
|
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { useState, useRef, useCallback } from 'react';
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
|
@ -55,6 +55,8 @@ export const DetailedStatus: React.FC<{
|
||||||
pictureInPicture: any;
|
pictureInPicture: any;
|
||||||
onToggleHidden?: (status: any) => void;
|
onToggleHidden?: (status: any) => void;
|
||||||
onToggleMediaVisibility?: () => void;
|
onToggleMediaVisibility?: () => void;
|
||||||
|
ancestors?: number;
|
||||||
|
multiColumn?: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
status,
|
status,
|
||||||
onOpenMedia,
|
onOpenMedia,
|
||||||
|
|
@ -69,6 +71,8 @@ export const DetailedStatus: React.FC<{
|
||||||
pictureInPicture,
|
pictureInPicture,
|
||||||
onToggleMediaVisibility,
|
onToggleMediaVisibility,
|
||||||
onToggleHidden,
|
onToggleHidden,
|
||||||
|
ancestors = 0,
|
||||||
|
multiColumn = false,
|
||||||
}) => {
|
}) => {
|
||||||
const properStatus = status?.get('reblog') ?? status;
|
const properStatus = status?.get('reblog') ?? status;
|
||||||
const [height, setHeight] = useState(0);
|
const [height, setHeight] = useState(0);
|
||||||
|
|
@ -123,6 +127,30 @@ export const DetailedStatus: React.FC<{
|
||||||
if (onTranslate) onTranslate(status);
|
if (onTranslate) onTranslate(status);
|
||||||
}, [onTranslate, status]);
|
}, [onTranslate, status]);
|
||||||
|
|
||||||
|
// The component is managed and will change if the status changes
|
||||||
|
// Ancestors can increase when loading a thread, in which case we want to scroll,
|
||||||
|
// or decrease if a post is deleted, in which case we don't want to mess with it
|
||||||
|
const previousAncestors = useRef(-1);
|
||||||
|
useEffect(() => {
|
||||||
|
if (nodeRef.current && previousAncestors.current < ancestors) {
|
||||||
|
nodeRef.current.scrollIntoView(true);
|
||||||
|
|
||||||
|
// In the single-column interface, `scrollIntoView` will put the post behind the header, so compensate for that.
|
||||||
|
if (!multiColumn) {
|
||||||
|
const offset = document
|
||||||
|
.querySelector('.column-header__wrapper')
|
||||||
|
?.getBoundingClientRect().bottom;
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
const scrollingElement = document.scrollingElement ?? document.body;
|
||||||
|
scrollingElement.scrollBy(0, -offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousAncestors.current = ancestors;
|
||||||
|
}, [ancestors, multiColumn]);
|
||||||
|
|
||||||
if (!properStatus) {
|
if (!properStatus) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,6 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
attachFullscreenListener(this.onFullScreenChange);
|
attachFullscreenListener(this.onFullScreenChange);
|
||||||
|
|
||||||
this._scrollStatusIntoView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||||
|
|
@ -487,35 +485,11 @@ class Status extends ImmutablePureComponent {
|
||||||
this.statusNode = c;
|
this.statusNode = c;
|
||||||
};
|
};
|
||||||
|
|
||||||
_scrollStatusIntoView () {
|
|
||||||
const { status, multiColumn } = this.props;
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
requestIdleCallback(() => {
|
|
||||||
this.statusNode?.scrollIntoView(true);
|
|
||||||
|
|
||||||
// In the single-column interface, `scrollIntoView` will put the post behind the header,
|
|
||||||
// so compensate for that.
|
|
||||||
if (!multiColumn) {
|
|
||||||
const offset = document.querySelector('.column-header__wrapper')?.getBoundingClientRect()?.bottom;
|
|
||||||
if (offset) {
|
|
||||||
const scrollingElement = document.scrollingElement || document.body;
|
|
||||||
scrollingElement.scrollBy(0, -offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, descendantsIds } = this.props;
|
||||||
|
|
||||||
const isSameStatus = status && (prevProps.status?.get('id') === status.get('id'));
|
const isSameStatus = status && (prevProps.status?.get('id') === status.get('id'));
|
||||||
|
|
||||||
if (status && (ancestorsIds.length > prevProps.ancestorsIds.length || !isSameStatus)) {
|
|
||||||
this._scrollStatusIntoView();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only highlight replies after the initial load
|
// Only highlight replies after the initial load
|
||||||
if (prevProps.descendantsIds.length && isSameStatus) {
|
if (prevProps.descendantsIds.length && isSameStatus) {
|
||||||
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
||||||
|
|
@ -619,6 +593,8 @@ class Status extends ImmutablePureComponent {
|
||||||
showMedia={this.state.showMedia}
|
showMedia={this.state.showMedia}
|
||||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||||
pictureInPicture={pictureInPicture}
|
pictureInPicture={pictureInPicture}
|
||||||
|
ancestors={this.props.ancestorsIds.length}
|
||||||
|
multiColumn={multiColumn}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionBar
|
<ActionBar
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user