diff --git a/app/soapbox/features/account_timeline/index.js b/app/soapbox/features/account_timeline/index.js deleted file mode 100644 index 907c54d68..000000000 --- a/app/soapbox/features/account_timeline/index.js +++ /dev/null @@ -1,174 +0,0 @@ -import { OrderedSet as ImmutableOrderedSet } from 'immutable'; -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { FormattedMessage } from 'react-intl'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router-dom'; - -import { fetchAccountByUsername } from 'soapbox/actions/accounts'; -import { fetchPatronAccount } from 'soapbox/actions/patron'; -import { getSettings } from 'soapbox/actions/settings'; -import { getSoapboxConfig } from 'soapbox/actions/soapbox'; -import { expandAccountFeaturedTimeline, expandAccountTimeline } from 'soapbox/actions/timelines'; -import MissingIndicator from 'soapbox/components/missing_indicator'; -import StatusList from 'soapbox/components/status_list'; -import { Card, CardBody, Spinner, Text } from 'soapbox/components/ui'; -import { makeGetStatusIds, findAccountByUsername } from 'soapbox/selectors'; -import { getFeatures } from 'soapbox/utils/features'; - -const makeMapStateToProps = () => { - const getStatusIds = makeGetStatusIds(); - - const mapStateToProps = (state, { params, withReplies = false }) => { - const username = params.username || ''; - const me = state.get('me'); - const accountFetchError = ((state.getIn(['accounts', -1, 'username']) || '').toLowerCase() === username.toLowerCase()); - const soapboxConfig = getSoapboxConfig(state); - const features = getFeatures(state.get('instance')); - - let accountId = -1; - let account = null; - let accountUsername = username; - let accountApId = null; - if (accountFetchError) { - accountId = null; - } else { - account = findAccountByUsername(state, username); - accountId = account ? account.getIn(['id'], null) : -1; - accountUsername = account ? account.getIn(['acct'], '') : ''; - accountApId = account ? account.get('url') : ''; - } - - const path = withReplies ? `${accountId}:with_replies` : accountId; - - const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false); - const unavailable = (me === accountId) ? false : (isBlocked && !features.blockersVisible); - const showPins = getSettings(state).getIn(['account_timeline', 'shows', 'pinned']) && !withReplies; - - return { - accountId, - unavailable, - accountUsername, - accountApId, - isBlocked, - account, - isAccount: !!state.getIn(['accounts', accountId]), - statusIds: getStatusIds(state, { type: `account:${path}`, prefix: 'account_timeline' }), - featuredStatusIds: showPins ? getStatusIds(state, { type: `account:${accountId}:pinned`, prefix: 'account_timeline' }) : ImmutableOrderedSet(), - isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']), - hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']), - me, - patronEnabled: soapboxConfig.getIn(['extensions', 'patron', 'enabled']), - }; - }; - - return mapStateToProps; -}; - -export default @connect(makeMapStateToProps) -@withRouter -class AccountTimeline extends ImmutablePureComponent { - - static propTypes = { - params: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - statusIds: ImmutablePropTypes.orderedSet, - featuredStatusIds: ImmutablePropTypes.orderedSet, - isLoading: PropTypes.bool, - hasMore: PropTypes.bool, - withReplies: PropTypes.bool, - isAccount: PropTypes.bool, - unavailable: PropTypes.bool, - }; - - componentDidMount() { - const { params: { username }, accountId, accountApId, withReplies, patronEnabled, history } = this.props; - - this.props.dispatch(fetchAccountByUsername(username, history)); - - if (accountId && accountId !== -1) { - if (!withReplies) { - this.props.dispatch(expandAccountFeaturedTimeline(accountId)); - } - - if (patronEnabled && accountApId) { - this.props.dispatch(fetchPatronAccount(accountApId)); - } - - this.props.dispatch(expandAccountTimeline(accountId, { withReplies })); - } - } - - componentDidUpdate(prevProps) { - const { params: { username }, accountId, withReplies, accountApId, patronEnabled, history } = this.props; - - if (username && (username !== prevProps.params.username)) { - this.props.dispatch(fetchAccountByUsername(username, history)); - } - - if (accountId && (accountId !== -1) && (accountId !== prevProps.accountId) || withReplies !== prevProps.withReplies) { - if (!withReplies) { - this.props.dispatch(expandAccountFeaturedTimeline(accountId)); - } - - if (patronEnabled && accountApId) { - this.props.dispatch(fetchPatronAccount(accountApId)); - } - - this.props.dispatch(expandAccountTimeline(accountId, { withReplies })); - } - } - - handleLoadMore = maxId => { - if (this.props.accountId && this.props.accountId !== -1) { - this.props.dispatch(expandAccountTimeline(this.props.accountId, { maxId, withReplies: this.props.withReplies })); - } - } - - render() { - const { statusIds, featuredStatusIds, isLoading, hasMore, isBlocked, isAccount, accountId, unavailable, accountUsername } = this.props; - - if (!isAccount && accountId !== -1) { - return ( - - ); - } - - if (accountId === -1 || (!statusIds && isLoading)) { - return ( - - ); - } - - if (unavailable) { - return ( - - - - {isBlocked ? ( - - ) : ( - - )} - - - - ); - } - - return ( - } - /> - ); - } - -} diff --git a/app/soapbox/features/account_timeline/index.tsx b/app/soapbox/features/account_timeline/index.tsx new file mode 100644 index 000000000..fc9110565 --- /dev/null +++ b/app/soapbox/features/account_timeline/index.tsx @@ -0,0 +1,108 @@ +import React, { useEffect } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { useHistory } from 'react-router-dom'; + +import { fetchAccountByUsername } from 'soapbox/actions/accounts'; +import { fetchPatronAccount } from 'soapbox/actions/patron'; +import { expandAccountFeaturedTimeline, expandAccountTimeline } from 'soapbox/actions/timelines'; +import MissingIndicator from 'soapbox/components/missing_indicator'; +import StatusList from 'soapbox/components/status_list'; +import { Card, CardBody, Spinner, Text } from 'soapbox/components/ui'; +import { useAppDispatch, useAppSelector, useFeatures, useSettings, useSoapboxConfig } from 'soapbox/hooks'; +import { makeGetStatusIds, findAccountByUsername } from 'soapbox/selectors'; + +const getStatusIds = makeGetStatusIds(); + +interface IAccountTimeline { + params: { + username: string, + }, + withReplies?: boolean, +} + +const AccountTimeline: React.FC = ({ params, withReplies = false }) => { + const history = useHistory(); + const dispatch = useAppDispatch(); + const features = useFeatures(); + const settings = useSettings(); + const soapboxConfig = useSoapboxConfig(); + + const account = useAppSelector(state => findAccountByUsername(state, params.username)); + + const path = withReplies ? `${account?.id}:with_replies` : account?.id; + const showPins = settings.getIn(['account_timeline', 'shows', 'pinned']) === true && !withReplies; + const statusIds = useAppSelector(state => getStatusIds(state, { type: `account:${path}`, prefix: 'account_timeline' })); + const featuredStatusIds = useAppSelector(state => getStatusIds(state, { type: `account:${account?.id}:pinned`, prefix: 'account_timeline' })); + + const isBlocked = useAppSelector(state => state.relationships.getIn([account?.id, 'blocked_by']) === true); + const unavailable = isBlocked && !features.blockersVisible; + const patronEnabled = soapboxConfig.getIn(['extensions', 'patron', 'enabled']) === true; + const isLoading = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'isLoading']) === true); + const hasMore = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'hasMore']) === true); + + const accountUsername = account?.username || params.username; + + useEffect(() => { + dispatch(fetchAccountByUsername(params.username, history)); + }, [params.username]); + + useEffect(() => { + if (account && !withReplies) { + dispatch(expandAccountFeaturedTimeline(account.id)); + } + }, [account?.id, withReplies]); + + useEffect(() => { + if (account && patronEnabled) { + dispatch(fetchPatronAccount(account.url)); + } + }, [account?.url, patronEnabled]); + + useEffect(() => { + if (account) { + dispatch(expandAccountTimeline(account.id, { withReplies })); + } + }, [account?.id]); + + const handleLoadMore = (maxId: string) => { + if (account) { + dispatch(expandAccountTimeline(account.id, { maxId, withReplies })); + } + }; + + if (!account && isLoading) { + return ; + } else if (!account) { + return ; + } + + if (unavailable) { + return ( + + + + {isBlocked ? ( + + ) : ( + + )} + + + + ); + } + + return ( + } + /> + ); +}; + +export default AccountTimeline;