Merge remote-tracking branch 'upstream' into glitch

This commit is contained in:
Laura Hausmann
2023-10-12 20:16:41 +02:00
192 changed files with 2415 additions and 2113 deletions
@@ -59,14 +59,14 @@ class Search extends PureComponent {
};
defaultOptions = [
{ label: <><mark>has:</mark> <FormattedList type='disjunction' value={['media', 'poll', 'embed']} /></>, action: e => { e.preventDefault(); this._insertText('has:') } },
{ label: <><mark>is:</mark> <FormattedList type='disjunction' value={['reply', 'sensitive']} /></>, action: e => { e.preventDefault(); this._insertText('is:') } },
{ label: <><mark>language:</mark> <FormattedMessage id='search_popout.language_code' defaultMessage='ISO language code' /></>, action: e => { e.preventDefault(); this._insertText('language:') } },
{ label: <><mark>from:</mark> <FormattedMessage id='search_popout.user' defaultMessage='user' /></>, action: e => { e.preventDefault(); this._insertText('from:') } },
{ label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:') } },
{ label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:') } },
{ label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:') } },
{ label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library']} /></>, action: e => { e.preventDefault(); this._insertText('in:') } }
{ label: <><mark>has:</mark> <FormattedList type='disjunction' value={['media', 'poll', 'embed']} /></>, action: e => { e.preventDefault(); this._insertText('has:'); } },
{ label: <><mark>is:</mark> <FormattedList type='disjunction' value={['reply', 'sensitive']} /></>, action: e => { e.preventDefault(); this._insertText('is:'); } },
{ label: <><mark>language:</mark> <FormattedMessage id='search_popout.language_code' defaultMessage='ISO language code' /></>, action: e => { e.preventDefault(); this._insertText('language:'); } },
{ label: <><mark>from:</mark> <FormattedMessage id='search_popout.user' defaultMessage='user' /></>, action: e => { e.preventDefault(); this._insertText('from:'); } },
{ label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:'); } },
{ label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:'); } },
{ label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:'); } },
{ label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library']} /></>, action: e => { e.preventDefault(); this._insertText('in:'); } }
];
setRef = c => {
@@ -92,7 +92,7 @@ class Search extends PureComponent {
if (value.length > 0 || submitted) {
onClear();
this.setState({ options: [], selectedOption: -1 })
this.setState({ options: [], selectedOption: -1 });
}
};
@@ -80,7 +80,7 @@ class Results extends PureComponent {
}
return null;
};
}
handleSelectAll = () => {
const { submittedType, dispatch } = this.props;
@@ -116,7 +116,7 @@ class Results extends PureComponent {
}
this.setState({ type: 'hashtags' });
}
};
handleSelectStatuses = () => {
const { submittedType, dispatch } = this.props;
@@ -128,7 +128,7 @@ class Results extends PureComponent {
}
this.setState({ type: 'statuses' });
}
};
handleLoadMoreAccounts = () => this._loadMore('accounts');
handleLoadMoreStatuses = () => this._loadMore('statuses');
@@ -221,7 +221,7 @@ const Firehose = ({ feedType, multiColumn }) => {
</Helmet>
</Column>
);
}
};
Firehose.propTypes = {
multiColumn: PropTypes.bool,
@@ -39,8 +39,17 @@ const getHomeFeedSpeed = createSelector([
], (statusIds, pendingStatusIds, statusMap) => {
const recentStatusIds = pendingStatusIds.size > 0 ? pendingStatusIds : statusIds;
const statuses = recentStatusIds.filter(id => id !== null).map(id => statusMap.get(id)).filter(status => status?.get('account') !== me).take(20);
const oldest = new Date(statuses.getIn([statuses.size - 1, 'created_at'], 0));
const newest = new Date(statuses.getIn([0, 'created_at'], 0));
if (statuses.isEmpty()) {
return {
gap: 0,
newest: new Date(0),
};
}
const datetimes = statuses.map(status => status.get('created_at', 0));
const oldest = new Date(datetimes.min());
const newest = new Date(datetimes.max());
const averageGap = (newest - oldest) / (1000 * (statuses.size + 1)); // Average gap between posts on first page in seconds
return {
@@ -27,9 +27,9 @@ const mapStateToProps = (state, { accountId }) => ({
const mapDispatchToProps = (dispatch) => ({
onSignupClick() {
dispatch(closeModal({
modalType: undefined,
ignoreFocus: false,
}));
modalType: undefined,
ignoreFocus: false,
}));
dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
},
});
@@ -187,7 +187,7 @@ class LoginForm extends React.PureComponent {
setIFrameRef = (iframe) => {
this.iframeRef = iframe;
}
};
handleFocus = () => {
this.setState({ expanded: true });
@@ -104,7 +104,7 @@ const Comment = ({ comment, domain, statusIds, isRemote, isSubmitting, selectedD
</div>
</>
);
}
};
Comment.propTypes = {
comment: PropTypes.string.isRequired,
@@ -217,6 +217,7 @@ class Status extends ImmutablePureComponent {
componentDidMount () {
attachFullscreenListener(this.onFullScreenChange);
this.props.dispatch(fetchStatus(this.props.params.statusId));
this._scrollStatusIntoView();
}
static getDerivedStateFromProps(props, state) {
@@ -614,10 +615,10 @@ class Status extends ImmutablePureComponent {
this.column = c;
};
componentDidUpdate (prevProps) {
const { status, ancestorsIds, multiColumn } = this.props;
_scrollStatusIntoView () {
const { status, multiColumn } = this.props;
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) {
if (status) {
window.requestAnimationFrame(() => {
this.node?.querySelector('.detailed-status__wrapper')?.scrollIntoView(true);
@@ -634,6 +635,14 @@ class Status extends ImmutablePureComponent {
}
}
componentDidUpdate (prevProps) {
const { status, ancestorsIds } = this.props;
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) {
this._scrollStatusIntoView();
}
}
componentWillUnmount () {
detachFullscreenListener(this.onFullScreenChange);
}
@@ -642,6 +651,22 @@ class Status extends ImmutablePureComponent {
this.setState({ fullscreen: isFullscreen() });
};
shouldUpdateScroll = (prevRouterProps, { location }) => {
// Do not change scroll when opening a modal
if (location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey) {
return false;
}
// Scroll to focused post if it is loaded
const child = this.node?.querySelector('.detailed-status__wrapper');
if (child) {
return [0, child.offsetTop];
}
// Do not scroll otherwise, `componentDidUpdate` will take care of that
return false;
};
render () {
let ancestors, descendants;
const { isLoading, status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
@@ -701,7 +726,7 @@ class Status extends ImmutablePureComponent {
)}
/>
<ScrollContainer scrollKey='thread'>
<ScrollContainer scrollKey='thread' shouldUpdateScroll={this.shouldUpdateScroll}>
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
{ancestors}
@@ -77,8 +77,8 @@ class Header extends PureComponent {
if (sso_redirect) {
content = (
<a href={sso_redirect} data-method='post' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sso_redirect' defaultMessage='Login or Register' /></a>
)
<a href={sso_redirect} data-method='post' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sso_redirect' defaultMessage='Login or Register' /></a>
);
} else {
let signupButton;
@@ -70,7 +70,7 @@ class LinkFooter extends PureComponent {
{DividingCircle}
<Link to='/keyboard-shortcuts'><FormattedMessage id='footer.keyboard_shortcuts' defaultMessage='Keyboard shortcuts' /></Link>
{DividingCircle}
v{version}
<span class='version'>v{version}</span>
</p>
</div>
);
@@ -128,7 +128,7 @@ export default class ModalRoot extends PureComponent {
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
{(SpecificComponent) => {
const ref = typeof SpecificComponent !== 'function' ? this.setModalRef : undefined;
return <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={ref} />
return <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={ref} />;
}}
</BundleContainer>
@@ -24,7 +24,7 @@ const SignInBanner = () => {
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.' /></p>
<a href={sso_redirect} data-method='post' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sso_redirect' defaultMessage='Login or Register' /></a>
</div>
)
);
}
if (registrationsOpen) {