Add UI support for disabled live feeds (#36577)

Co-authored-by: diondiondion <mail@diondiondion.com>
pull/36580/head
Claire 2 weeks ago committed by GitHub
parent f7b99cd48a
commit 2fa5dd6d1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -10,7 +10,8 @@ import { connect } from 'react-redux';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; 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 { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { connectCommunityStream } from '../../actions/streaming'; import { connectCommunityStream } from '../../actions/streaming';
@ -120,8 +121,21 @@ class CommunityTimeline extends PureComponent {
render () { render () {
const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props; const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
const { signedIn, permissions } = this.props.identity;
const pinned = !!columnId; 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 ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
@ -144,7 +158,7 @@ class CommunityTimeline extends PureComponent {
scrollKey={`community_timeline-${columnId}`} scrollKey={`community_timeline-${columnId}`}
timelineId={`community${onlyMedia ? ':media' : ''}`} timelineId={`community${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore} 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} bindToDocument={!multiColumn}
/> />

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

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

@ -10,7 +10,8 @@ import { connect } from 'react-redux';
import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; 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 { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { connectPublicStream } from '../../actions/streaming'; import { connectPublicStream } from '../../actions/streaming';
@ -123,8 +124,21 @@ class PublicTimeline extends PureComponent {
render () { render () {
const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props; const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
const { signedIn, permissions } = this.props.identity;
const pinned = !!columnId; 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 ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
@ -147,7 +161,7 @@ class PublicTimeline extends PureComponent {
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
trackScroll={!pinned} trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`} 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} bindToDocument={!multiColumn}
/> />

@ -33,10 +33,10 @@ interface InitialStateMeta {
single_user_mode: boolean; single_user_mode: boolean;
source_url: string; source_url: string;
streaming_api_base_url: string; streaming_api_base_url: string;
local_live_feed_access: 'public' | 'authenticated'; local_live_feed_access: 'public' | 'authenticated' | 'disabled';
remote_live_feed_access: 'public' | 'authenticated'; remote_live_feed_access: 'public' | 'authenticated' | 'disabled';
local_topic_feed_access: 'public' | 'authenticated'; local_topic_feed_access: 'public' | 'authenticated' | 'disabled';
remote_topic_feed_access: 'public' | 'authenticated'; remote_topic_feed_access: 'public' | 'authenticated' | 'disabled';
title: string; title: string;
show_trends: boolean; show_trends: boolean;
trends_as_landing_page: boolean; trends_as_landing_page: boolean;

@ -333,6 +333,7 @@
"empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.", "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
"empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.", "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
"empty_column.disabled_feed": "This feed has been disabled by your server administrators.",
"empty_column.domain_blocks": "There are no blocked domains yet.", "empty_column.domain_blocks": "There are no blocked domains yet.",
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!", "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
"empty_column.favourited_statuses": "You don't have any favorite posts yet. When you favorite one, it will show up here.", "empty_column.favourited_statuses": "You don't have any favorite posts yet. When you favorite one, it will show up here.",

@ -1,3 +1,4 @@
export const PEMRISSION_VIEW_FEEDS = 0x0000000000100000;
export const PERMISSION_INVITE_USERS = 0x0000000000010000; export const PERMISSION_INVITE_USERS = 0x0000000000010000;
export const PERMISSION_MANAGE_USERS = 0x0000000000000400; export const PERMISSION_MANAGE_USERS = 0x0000000000000400;
export const PERMISSION_MANAGE_TAXONOMIES = 0x0000000000000100; export const PERMISSION_MANAGE_TAXONOMIES = 0x0000000000000100;
@ -22,3 +23,19 @@ export function canManageReports(permissions: number) {
(permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS (permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS
); );
} }
export const canViewFeed = (
signedIn: boolean,
permissions: number,
setting: 'public' | 'authenticated' | 'disabled' | undefined,
) => {
switch (setting) {
case 'public':
return true;
case 'authenticated':
return signedIn;
case 'disabled':
default:
return (permissions & PEMRISSION_VIEW_FEEDS) === PEMRISSION_VIEW_FEEDS;
}
};

Loading…
Cancel
Save