Add UI support for disabled live feeds (#36577)
Co-authored-by: diondiondion <mail@diondiondion.com>
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user