diff --git a/app/soapbox/features/home_timeline/index.js b/app/soapbox/features/home_timeline/index.js deleted file mode 100644 index f93887713..000000000 --- a/app/soapbox/features/home_timeline/index.js +++ /dev/null @@ -1,112 +0,0 @@ -import { OrderedSet as ImmutableOrderedSet } from 'immutable'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import { connect } from 'react-redux'; -import { Link } from 'react-router-dom'; - -import { getFeatures } from 'soapbox/utils/features'; - -import { expandHomeTimeline } from '../../actions/timelines'; -import { Column } from '../../components/ui'; -import Timeline from '../ui/components/timeline'; - -const messages = defineMessages({ - title: { id: 'column.home', defaultMessage: 'Home' }, -}); - -const mapStateToProps = state => { - const instance = state.get('instance'); - const features = getFeatures(instance); - - return { - hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0, - isPartial: state.getIn(['timelines', 'home', 'isPartial']), - siteTitle: state.getIn(['instance', 'title']), - isLoading: state.getIn(['timelines', 'home', 'isLoading'], true), - loadingFailed: state.getIn(['timelines', 'home', 'loadingFailed'], false), - isEmpty: state.getIn(['timelines', 'home', 'items'], ImmutableOrderedSet()).isEmpty(), - features, - }; -}; - -export default @connect(mapStateToProps) -@injectIntl -class HomeTimeline extends React.PureComponent { - - static propTypes = { - dispatch: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - hasUnread: PropTypes.bool, - isPartial: PropTypes.bool, - siteTitle: PropTypes.string, - isLoading: PropTypes.bool, - loadingFailed: PropTypes.bool, - isEmpty: PropTypes.bool, - features: PropTypes.object.isRequired, - }; - - state = { - done: false, - } - - handleLoadMore = maxId => { - this.props.dispatch(expandHomeTimeline({ maxId })); - } - - componentDidMount() { - this._checkIfReloadNeeded(false, this.props.isPartial); - } - - componentDidUpdate(prevProps) { - this._checkIfReloadNeeded(prevProps.isPartial, this.props.isPartial); - } - - componentWillUnmount() { - this._stopPolling(); - } - - _checkIfReloadNeeded(wasPartial, isPartial) { - const { dispatch } = this.props; - - if (wasPartial === isPartial) { - return; - } else if (!wasPartial && isPartial) { - this.polling = setInterval(() => { - dispatch(expandHomeTimeline()); - }, 3000); - } else if (wasPartial && !isPartial) { - this._stopPolling(); - } - } - - _stopPolling() { - if (this.polling) { - clearInterval(this.polling); - this.polling = null; - } - } - - handleRefresh = () => { - const { dispatch } = this.props; - return dispatch(expandHomeTimeline()); - } - - render() { - const { intl, siteTitle } = this.props; - - return ( - - }} />} - /> - - ); - } - -} diff --git a/app/soapbox/features/home_timeline/index.tsx b/app/soapbox/features/home_timeline/index.tsx new file mode 100644 index 000000000..79cca2b9d --- /dev/null +++ b/app/soapbox/features/home_timeline/index.tsx @@ -0,0 +1,71 @@ +import React, { useEffect, useRef } from 'react'; +import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; +import { Link } from 'react-router-dom'; + +import { expandHomeTimeline } from 'soapbox/actions/timelines'; +import { Column } from 'soapbox/components/ui'; +import Timeline from 'soapbox/features/ui/components/timeline'; +import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; + +const messages = defineMessages({ + title: { id: 'column.home', defaultMessage: 'Home' }, +}); + +const HomeTimeline: React.FC = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const polling = useRef(null); + + const isPartial = useAppSelector(state => state.timelines.getIn(['home', 'isPartial']) === true); + const siteTitle = useAppSelector(state => state.instance.title); + + const handleLoadMore = (maxId: string) => { + dispatch(expandHomeTimeline({ maxId })); + }; + + useEffect(() => { + checkIfReloadNeeded(); + + return () => { + stopPolling(); + }; + }, [isPartial]); + + // Mastodon generates the feed in Redis, and can return a partial timeline + // (HTTP 206) for new users. Poll until we get a full page of results. + const checkIfReloadNeeded = () => { + if (isPartial) { + polling.current = setInterval(() => { + dispatch(expandHomeTimeline()); + }, 3000); + } else { + stopPolling(); + } + }; + + const stopPolling = () => { + if (polling.current) { + clearInterval(polling.current); + polling.current = null; + } + }; + + const handleRefresh = () => { + return dispatch(expandHomeTimeline()); + }; + + return ( + + }} />} + /> + + ); +}; + +export default HomeTimeline;