Merge commit '1ba579b0a181fbfff514ef32b50179d2ab1fc342' into glitch-soc/merge-upstream

Conflicts:
- `app/models/public_feed.rb`:
  Minor conflict due to glitch-soc's local-only posts.
  Adopted upstream's changes.
- `spec/models/tag_feed_spec.rb`:
  Minor conflict due to glitch-soc's local-only posts.
  Adopted upstream's changes.
This commit is contained in:
Claire
2025-10-23 18:18:11 +02:00
77 changed files with 789 additions and 87 deletions

View File

@@ -10,7 +10,8 @@ import { connect } from 'react-redux';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain } from 'mastodon/initial_state';
import { domain, localLiveFeedAccess } from 'mastodon/initial_state';
import { canViewFeed } from 'mastodon/permissions';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { connectCommunityStream } from '../../actions/streaming';
@@ -120,8 +121,21 @@ class CommunityTimeline extends PureComponent {
render () {
const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
const { signedIn, permissions } = this.props.identity;
const pinned = !!columnId;
const emptyMessage = canViewFeed(signedIn, permissions, localLiveFeedAccess) ? (
<FormattedMessage
id='empty_column.community'
defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!'
/>
) : (
<FormattedMessage
id='empty_column.disabled_feed'
defaultMessage='This feed has been disabled by your server administrators.'
/>
);
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
@@ -144,7 +158,7 @@ class CommunityTimeline extends PureComponent {
scrollKey={`community_timeline-${columnId}`}
timelineId={`community${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
/>

View File

@@ -13,7 +13,8 @@ import { changeSetting } from 'mastodon/actions/settings';
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { localLiveFeedAccess, remoteLiveFeedAccess, me, domain } from 'mastodon/initial_state';
import { localLiveFeedAccess, remoteLiveFeedAccess, domain } from 'mastodon/initial_state';
import { canViewFeed } from 'mastodon/permissions';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import Column from '../../components/column';
@@ -52,7 +53,7 @@ const ColumnSettings = () => {
const Firehose = ({ feedType, multiColumn }) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const { signedIn } = useIdentity();
const { signedIn, permissions } = useIdentity();
const columnRef = useRef(null);
const onlyMedia = useAppSelector((state) => state.getIn(['settings', 'firehose', 'onlyMedia'], false));
@@ -151,6 +152,15 @@ const Firehose = ({ feedType, multiColumn }) => {
/>
);
const canViewSelectedFeed = canViewFeed(signedIn, permissions, feedType === 'community' ? localLiveFeedAccess : remoteLiveFeedAccess);
const disabledTimelineMessage = (
<FormattedMessage
id='empty_column.disabled_feed'
defaultMessage='This feed has been disabled by your server administrators.'
/>
);
return (
<Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
@@ -165,7 +175,7 @@ const Firehose = ({ feedType, multiColumn }) => {
<ColumnSettings />
</ColumnHeader>
{(signedIn || (localLiveFeedAccess === 'public' && remoteLiveFeedAccess === 'public')) && (
{(canViewFeed(signedIn, permissions, localLiveFeedAccess) && canViewFeed(signedIn, permissions, remoteLiveFeedAccess)) && (
<div className='account__section-headline'>
<NavLink exact to='/public/local'>
<FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
@@ -187,7 +197,7 @@ const Firehose = ({ feedType, multiColumn }) => {
onLoadMore={handleLoadMore}
trackScroll
scrollKey='firehose'
emptyMessage={emptyMessage}
emptyMessage={canViewSelectedFeed ? emptyMessage : disabledTimelineMessage}
bindToDocument={!multiColumn}
/>

View File

@@ -42,6 +42,7 @@ import {
me,
} from 'mastodon/initial_state';
import { transientSingleColumn } from 'mastodon/is_mobile';
import { canViewFeed } from 'mastodon/permissions';
import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifications';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
@@ -194,7 +195,7 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
multiColumn = false,
}) => {
const intl = useIntl();
const { signedIn, disabledAccountId } = useIdentity();
const { signedIn, permissions, disabledAccountId } = useIdentity();
const location = useLocation();
const showSearch = useBreakpoint('full') && !multiColumn;
@@ -262,13 +263,12 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
/>
)}
{(signedIn ||
localLiveFeedAccess === 'public' ||
remoteLiveFeedAccess === 'public') && (
{(canViewFeed(signedIn, permissions, localLiveFeedAccess) ||
canViewFeed(signedIn, permissions, remoteLiveFeedAccess)) && (
<ColumnLink
transparent
to={
signedIn || localLiveFeedAccess === 'public'
canViewFeed(signedIn, permissions, localLiveFeedAccess)
? '/public/local'
: '/public/remote'
}

View File

@@ -10,7 +10,8 @@ import { connect } from 'react-redux';
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain } from 'mastodon/initial_state';
import { domain, localLiveFeedAccess, remoteLiveFeedAccess } from 'mastodon/initial_state';
import { canViewFeed } from 'mastodon/permissions';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { connectPublicStream } from '../../actions/streaming';
@@ -123,8 +124,21 @@ class PublicTimeline extends PureComponent {
render () {
const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
const { signedIn, permissions } = this.props.identity;
const pinned = !!columnId;
const emptyMessage = (canViewFeed(signedIn, permissions, localLiveFeedAccess) || canViewFeed(signedIn, permissions, remoteLiveFeedAccess)) ? (
<FormattedMessage
id='empty_column.public'
defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up'
/>
) : (
<FormattedMessage
id='empty_column.disabled_feed'
defaultMessage='This feed has been disabled by your server administrators.'
/>
);
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
@@ -147,7 +161,7 @@ class PublicTimeline extends PureComponent {
onLoadMore={this.handleLoadMore}
trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`}
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
/>

View File

@@ -504,12 +504,14 @@ class Status extends ImmutablePureComponent {
componentDidUpdate (prevProps) {
const { status, ancestorsIds, descendantsIds } = this.props;
if (status && (ancestorsIds.length > prevProps.ancestorsIds.length || 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
if (prevProps.descendantsIds.length) {
if (prevProps.descendantsIds.length && isSameStatus) {
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
if (newRepliesIds.length) {