Merge commit '02de05dc272856c77fac5c9e1d84cf585ae20c1e' into glitch-soc/merge-upstream
This commit is contained in:
commit
884d7996b1
50 files changed
+653
-96
No files matched your search
@@ -0,0 +1,113 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
|
||||
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
|
||||
import { fetchQuotes } from 'mastodon/actions/interactions_typed';
|
||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import StatusList from 'mastodon/components/status_list';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import Column from '../ui/components/column';
|
||||
|
||||
const messages = defineMessages({
|
||||
refresh: { id: 'refresh', defaultMessage: 'Refresh' },
|
||||
});
|
||||
|
||||
const emptyList = ImmutableList();
|
||||
|
||||
export const Quotes: React.FC<{
|
||||
multiColumn?: boolean;
|
||||
params?: { statusId: string };
|
||||
}> = ({ multiColumn, params }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const statusId = params?.statusId;
|
||||
|
||||
const isCorrectStatusId: boolean = useAppSelector(
|
||||
(state) => state.status_lists.getIn(['quotes', 'statusId']) === statusId,
|
||||
);
|
||||
const statusIds = useAppSelector((state) =>
|
||||
state.status_lists.getIn(['quotes', 'items'], emptyList),
|
||||
);
|
||||
const nextUrl = useAppSelector(
|
||||
(state) =>
|
||||
state.status_lists.getIn(['quotes', 'next']) as string | undefined,
|
||||
);
|
||||
const isLoading = useAppSelector((state) =>
|
||||
state.status_lists.getIn(['quotes', 'isLoading'], true),
|
||||
);
|
||||
const hasMore = !!nextUrl;
|
||||
|
||||
useEffect(() => {
|
||||
if (statusId) void dispatch(fetchQuotes({ statusId }));
|
||||
}, [dispatch, statusId]);
|
||||
|
||||
const handleLoadMore = useCallback(() => {
|
||||
if (statusId && isCorrectStatusId && nextUrl)
|
||||
void dispatch(fetchQuotes({ statusId, next: nextUrl }));
|
||||
}, [dispatch, statusId, isCorrectStatusId, nextUrl]);
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
if (statusId) void dispatch(fetchQuotes({ statusId }));
|
||||
}, [dispatch, statusId]);
|
||||
|
||||
if (!statusIds || !isCorrectStatusId) {
|
||||
return (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = (
|
||||
<FormattedMessage
|
||||
id='status.quotes.empty'
|
||||
defaultMessage='No one has quoted this post yet. When someone does, it will show up here.'
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn}>
|
||||
<ColumnHeader
|
||||
showBackButton
|
||||
multiColumn={multiColumn}
|
||||
extraButton={
|
||||
<button
|
||||
type='button'
|
||||
className='column-header__button'
|
||||
title={intl.formatMessage(messages.refresh)}
|
||||
aria-label={intl.formatMessage(messages.refresh)}
|
||||
onClick={handleRefresh}
|
||||
>
|
||||
<Icon id='refresh' icon={RefreshIcon} />
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
|
||||
<StatusList
|
||||
scrollKey='quotes_timeline'
|
||||
statusIds={statusIds}
|
||||
onLoadMore={handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
/>
|
||||
|
||||
<Helmet>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default Quotes;
|
||||
@@ -32,7 +32,7 @@ const Embed: React.FC<{ id: string }> = ({ id }) => {
|
||||
const dispatchRenderSignal = useRenderSignal();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchStatus(id, false, false));
|
||||
dispatch(fetchStatus(id, { alsoFetchContext: false }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
const handleToggleHidden = useCallback(() => {
|
||||
|
||||
@@ -31,6 +31,7 @@ import { VisibilityIcon } from 'mastodon/components/visibility_icon';
|
||||
import { Audio } from 'mastodon/features/audio';
|
||||
import scheduleIdleTask from 'mastodon/features/ui/util/schedule_idle_task';
|
||||
import { Video } from 'mastodon/features/video';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
|
||||
import Card from './card';
|
||||
|
||||
@@ -282,6 +283,22 @@ export const DetailedStatus: React.FC<{
|
||||
|
||||
if (['private', 'direct'].includes(status.get('visibility') as string)) {
|
||||
quotesLink = '';
|
||||
} else if (status.getIn(['account', 'id']) === me) {
|
||||
quotesLink = (
|
||||
<Link
|
||||
to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/quotes`}
|
||||
className='detailed-status__link'
|
||||
>
|
||||
<span className='detailed-status__quotes'>
|
||||
<AnimatedNumber value={status.get('quotes_count')} />
|
||||
</span>
|
||||
<FormattedMessage
|
||||
id='status.quotes'
|
||||
defaultMessage='{count, plural, one {quote} other {quotes}}'
|
||||
values={{ count: status.get('quotes_count') }}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
quotesLink = (
|
||||
<span className='detailed-status__link'>
|
||||
@@ -399,7 +416,10 @@ export const DetailedStatus: React.FC<{
|
||||
{hashtagBar}
|
||||
|
||||
{status.get('quote') && (
|
||||
<QuotedStatus quote={status.get('quote')} />
|
||||
<QuotedStatus
|
||||
quote={status.get('quote')}
|
||||
parentQuotePostId={status.get('id')}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -38,7 +38,7 @@ class FilterModal extends ImmutablePureComponent {
|
||||
|
||||
handleSuccess = () => {
|
||||
const { dispatch, statusId } = this.props;
|
||||
dispatch(fetchStatus(statusId, true));
|
||||
dispatch(fetchStatus(statusId, {forceFetch: true}));
|
||||
this.setState({ isSubmitting: false, isSubmitted: true, step: 'submitted' });
|
||||
};
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ import {
|
||||
PrivacyPolicy,
|
||||
TermsOfService,
|
||||
AccountFeatured,
|
||||
Quotes,
|
||||
} from './util/async-components';
|
||||
import { ColumnsContextProvider } from './util/columns_context';
|
||||
import { focusColumn, getFocusedItemIndex, focusItemSibling } from './util/focusUtils';
|
||||
@@ -209,6 +210,7 @@ class SwitchingColumnsArea extends PureComponent {
|
||||
<WrappedRoute path='/@:acct/:statusId' exact component={Status} content={children} />
|
||||
<WrappedRoute path='/@:acct/:statusId/reblogs' component={Reblogs} content={children} />
|
||||
<WrappedRoute path='/@:acct/:statusId/favourites' component={Favourites} content={children} />
|
||||
<WrappedRoute path='/@:acct/:statusId/quotes' component={Quotes} content={children} />
|
||||
|
||||
{/* Legacy routes, cannot be easily factored with other routes because they share a param name */}
|
||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
|
||||
|
||||
@@ -86,6 +86,10 @@ export function Favourites () {
|
||||
return import('../../favourites');
|
||||
}
|
||||
|
||||
export function Quotes () {
|
||||
return import('../../quotes');
|
||||
}
|
||||
|
||||
export function FollowRequests () {
|
||||
return import('../../follow_requests');
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user