From 1794e5a72d1bd41fc91028c0a76843558fb51016 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 24 Dec 2020 16:20:58 -0600 Subject: [PATCH] Rudimentary remote timelines --- app/soapbox/actions/streaming.js | 3 +- app/soapbox/actions/timelines.js | 2 + .../components/column_settings.js | 36 +++++++ .../containers/column_settings_container.js | 17 +++ app/soapbox/features/remote_timeline/index.js | 101 ++++++++++++++++++ app/soapbox/features/ui/index.js | 2 + .../features/ui/util/async-components.js | 4 + 7 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 app/soapbox/features/remote_timeline/components/column_settings.js create mode 100644 app/soapbox/features/remote_timeline/containers/column_settings_container.js create mode 100644 app/soapbox/features/remote_timeline/index.js diff --git a/app/soapbox/actions/streaming.js b/app/soapbox/actions/streaming.js index 3b66a0472..1baa252b0 100644 --- a/app/soapbox/actions/streaming.js +++ b/app/soapbox/actions/streaming.js @@ -82,7 +82,8 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => { export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification); export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`); export const connectPublicStream = ({ onlyMedia } = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`); +export const connectRemoteStream = (instance, { onlyMedia } = {}) => connectTimelineStream(`remote${onlyMedia ? ':media' : ''}:${instance}`, `public:remote${onlyMedia ? ':media' : ''}&instance=${instance}`); export const connectHashtagStream = (id, tag, accept) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept); export const connectDirectStream = () => connectTimelineStream('direct', 'direct'); export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`); -export const connectGroupStream = id => connectTimelineStream(`group:${id}`, `group&group=${id}`); +export const connectGroupStream = id => connectTimelineStream(`group:${id}`, `group&group=${id}`); diff --git a/app/soapbox/actions/timelines.js b/app/soapbox/actions/timelines.js index 67f6b19f8..7ce6320fd 100644 --- a/app/soapbox/actions/timelines.js +++ b/app/soapbox/actions/timelines.js @@ -166,6 +166,8 @@ export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => ex export const expandPublicTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done); +export const expandRemoteTimeline = (instance, { maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`remote${onlyMedia ? ':media' : ''}:${instance}`, '/api/v1/timelines/public', { local: false, instance: instance, max_id: maxId, only_media: !!onlyMedia }, done); + export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done); export const expandDirectTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done); diff --git a/app/soapbox/features/remote_timeline/components/column_settings.js b/app/soapbox/features/remote_timeline/components/column_settings.js new file mode 100644 index 000000000..1d7bd7359 --- /dev/null +++ b/app/soapbox/features/remote_timeline/components/column_settings.js @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { injectIntl, FormattedMessage } from 'react-intl'; +import SettingToggle from '../../notifications/components/setting_toggle'; + +export default @injectIntl +class ColumnSettings extends React.PureComponent { + + static propTypes = { + settings: ImmutablePropTypes.map.isRequired, + onChange: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + render() { + const { settings, onChange } = this.props; + + return ( +
+
+ } /> +
+ +
+ } /> +
+ +
+ } /> +
+
+ ); + } + +} diff --git a/app/soapbox/features/remote_timeline/containers/column_settings_container.js b/app/soapbox/features/remote_timeline/containers/column_settings_container.js new file mode 100644 index 000000000..ac001bcab --- /dev/null +++ b/app/soapbox/features/remote_timeline/containers/column_settings_container.js @@ -0,0 +1,17 @@ +import { connect } from 'react-redux'; +import ColumnSettings from '../components/column_settings'; +import { getSettings, changeSetting } from '../../../actions/settings'; + +const mapStateToProps = state => ({ + settings: getSettings(state).get('public'), +}); + +const mapDispatchToProps = (dispatch) => { + return { + onChange(key, checked) { + dispatch(changeSetting(['public', ...key], checked)); + }, + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); diff --git a/app/soapbox/features/remote_timeline/index.js b/app/soapbox/features/remote_timeline/index.js new file mode 100644 index 000000000..b5d6cdb8f --- /dev/null +++ b/app/soapbox/features/remote_timeline/index.js @@ -0,0 +1,101 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; +import StatusListContainer from '../ui/containers/status_list_container'; +import Column from '../../components/column'; +import ColumnSettingsContainer from './containers/column_settings_container'; +import HomeColumnHeader from '../../components/home_column_header'; +import { expandRemoteTimeline } from '../../actions/timelines'; +import { connectRemoteStream } from '../../actions/streaming'; +import { getSettings } from 'soapbox/actions/settings'; + +const messages = defineMessages({ + title: { id: 'column.remote', defaultMessage: 'Federated timeline' }, +}); + +const mapStateToProps = (state, props) => { + const instance = props.params.instance; + const settings = getSettings(state); + const onlyMedia = settings.getIn(['remote', 'other', 'onlyMedia']); + + const timelineId = 'remote'; + + return { + timelineId, + onlyMedia, + hasUnread: state.getIn(['timelines', `${timelineId}${onlyMedia ? ':media' : ''}:${instance}`, 'unread']) > 0, + instance, + }; +}; + +export default @connect(mapStateToProps) +@injectIntl +class RemoteTimeline extends React.PureComponent { + + static contextTypes = { + router: PropTypes.object, + }; + + static propTypes = { + dispatch: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + hasUnread: PropTypes.bool, + onlyMedia: PropTypes.bool, + timelineId: PropTypes.string, + instance: PropTypes.string.isRequired, + }; + + componentDidMount() { + const { dispatch, onlyMedia, instance } = this.props; + dispatch(expandRemoteTimeline(instance, { onlyMedia })); + this.disconnect = dispatch(connectRemoteStream(instance, { onlyMedia })); + } + + componentDidUpdate(prevProps) { + if (prevProps.onlyMedia !== this.props.onlyMedia) { + const { dispatch, onlyMedia, instance } = this.props; + this.disconnect(); + + dispatch(expandRemoteTimeline(instance, { onlyMedia })); + this.disconnect = dispatch(connectRemoteStream(instance, { onlyMedia })); + } + } + + componentWillUnmount() { + if (this.disconnect) { + this.disconnect(); + this.disconnect = null; + } + } + + handleLoadMore = maxId => { + const { dispatch, onlyMedia, instance } = this.props; + dispatch(expandRemoteTimeline(instance, { maxId, onlyMedia })); + } + + render() { + const { intl, hasUnread, onlyMedia, timelineId, instance } = this.props; + + return ( + + + + + + } + /> + + ); + } + +} diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index 9f0085772..8b714fe7e 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -45,6 +45,7 @@ import { // GettingStarted, CommunityTimeline, PublicTimeline, + RemoteTimeline, AccountTimeline, AccountGallery, HomeTimeline, @@ -208,6 +209,7 @@ class SwitchingColumnsArea extends React.PureComponent { + {/* diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js index 01838b156..3cb9e5142 100644 --- a/app/soapbox/features/ui/util/async-components.js +++ b/app/soapbox/features/ui/util/async-components.js @@ -18,6 +18,10 @@ export function PublicTimeline() { return import(/* webpackChunkName: "features/public_timeline" */'../../public_timeline'); } +export function RemoteTimeline() { + return import(/* webpackChunkName: "features/remote_timeline" */'../../remote_timeline'); +} + export function CommunityTimeline() { return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline'); }