123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- import PropTypes from 'prop-types';
- import { useRef, useCallback, useEffect } from 'react';
- import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
- import { Helmet } from 'react-helmet';
- import { NavLink } from 'react-router-dom';
- import PublicIcon from '@/material-icons/400-24px/public.svg?react';
- import { addColumn } from 'mastodon/actions/columns';
- 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 initialState, { domain } from 'mastodon/initial_state';
- import { useAppDispatch, useAppSelector } from 'mastodon/store';
- import Column from '../../components/column';
- import ColumnHeader from '../../components/column_header';
- import SettingToggle from '../notifications/components/setting_toggle';
- import StatusListContainer from '../ui/containers/status_list_container';
- const messages = defineMessages({
- title: { id: 'column.firehose', defaultMessage: 'Live feeds' },
- });
- // TODO: use a proper React context later on
- const useIdentity = () => ({
- signedIn: !!initialState.meta.me,
- accountId: initialState.meta.me,
- disabledAccountId: initialState.meta.disabled_account_id,
- accessToken: initialState.meta.access_token,
- permissions: initialState.role ? initialState.role.permissions : 0,
- });
- const ColumnSettings = () => {
- const dispatch = useAppDispatch();
- const settings = useAppSelector((state) => state.getIn(['settings', 'firehose']));
- const onChange = useCallback(
- (key, checked) => dispatch(changeSetting(['firehose', ...key], checked)),
- [dispatch],
- );
- return (
- <div>
- <div className='column-settings__row'>
- <SettingToggle
- settings={settings}
- settingPath={['onlyMedia']}
- onChange={onChange}
- label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />}
- />
- </div>
- </div>
- );
- };
- const Firehose = ({ feedType, multiColumn }) => {
- const dispatch = useAppDispatch();
- const intl = useIntl();
- const { signedIn } = useIdentity();
- const columnRef = useRef(null);
- const onlyMedia = useAppSelector((state) => state.getIn(['settings', 'firehose', 'onlyMedia'], false));
- const hasUnread = useAppSelector((state) => state.getIn(['timelines', `${feedType}${onlyMedia ? ':media' : ''}`, 'unread'], 0) > 0);
- const handlePin = useCallback(
- () => {
- switch(feedType) {
- case 'community':
- dispatch(addColumn('COMMUNITY', { other: { onlyMedia } }));
- break;
- case 'public':
- dispatch(addColumn('PUBLIC', { other: { onlyMedia } }));
- break;
- case 'public:remote':
- dispatch(addColumn('REMOTE', { other: { onlyMedia, onlyRemote: true } }));
- break;
- }
- },
- [dispatch, onlyMedia, feedType],
- );
- const handleLoadMore = useCallback(
- (maxId) => {
- switch(feedType) {
- case 'community':
- dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
- break;
- case 'public':
- dispatch(expandPublicTimeline({ maxId, onlyMedia }));
- break;
- case 'public:remote':
- dispatch(expandPublicTimeline({ maxId, onlyMedia, onlyRemote: true }));
- break;
- }
- },
- [dispatch, onlyMedia, feedType],
- );
- const handleHeaderClick = useCallback(() => columnRef.current?.scrollTop(), []);
- useEffect(() => {
- let disconnect;
- switch(feedType) {
- case 'community':
- dispatch(expandCommunityTimeline({ onlyMedia }));
- if (signedIn) {
- disconnect = dispatch(connectCommunityStream({ onlyMedia }));
- }
- break;
- case 'public':
- dispatch(expandPublicTimeline({ onlyMedia }));
- if (signedIn) {
- disconnect = dispatch(connectPublicStream({ onlyMedia }));
- }
- break;
- case 'public:remote':
- dispatch(expandPublicTimeline({ onlyMedia, onlyRemote: true }));
- if (signedIn) {
- disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote: true }));
- }
- break;
- }
- return () => disconnect?.();
- }, [dispatch, signedIn, feedType, onlyMedia]);
- const prependBanner = feedType === 'community' ? (
- <DismissableBanner id='community_timeline'>
- <FormattedMessage
- id='dismissable_banner.community_timeline'
- defaultMessage='These are the most recent public posts from people whose accounts are hosted by {domain}.'
- values={{ domain }}
- />
- </DismissableBanner>
- ) : (
- <DismissableBanner id='public_timeline'>
- <FormattedMessage
- id='dismissable_banner.public_timeline'
- defaultMessage='These are the most recent public posts from people on the social web that people on {domain} follow.'
- values={{ domain }}
- />
- </DismissableBanner>
- );
- const emptyMessage = feedType === 'community' ? (
- <FormattedMessage
- id='empty_column.community'
- defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!'
- />
- ) : (
- <FormattedMessage
- id='empty_column.public'
- defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up'
- />
- );
- return (
- <Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
- <ColumnHeader
- icon='globe'
- iconComponent={PublicIcon}
- active={hasUnread}
- title={intl.formatMessage(messages.title)}
- onPin={handlePin}
- onClick={handleHeaderClick}
- multiColumn={multiColumn}
- >
- <ColumnSettings />
- </ColumnHeader>
- <div className='account__section-headline'>
- <NavLink exact to='/public/local'>
- <FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
- </NavLink>
- <NavLink exact to='/public/remote'>
- <FormattedMessage tagName='div' id='firehose.remote' defaultMessage='Other servers' />
- </NavLink>
- <NavLink exact to='/public'>
- <FormattedMessage tagName='div' id='firehose.all' defaultMessage='All' />
- </NavLink>
- </div>
- <StatusListContainer
- prepend={prependBanner}
- timelineId={`${feedType}${onlyMedia ? ':media' : ''}`}
- onLoadMore={handleLoadMore}
- trackScroll
- scrollKey='firehose'
- emptyMessage={emptyMessage}
- bindToDocument={!multiColumn}
- />
- <Helmet>
- <title>{intl.formatMessage(messages.title)}</title>
- <meta name='robots' content='noindex' />
- </Helmet>
- </Column>
- );
- };
- Firehose.propTypes = {
- multiColumn: PropTypes.bool,
- feedType: PropTypes.string,
- };
- export default Firehose;
|