[Glitch] Federate pinned statuses over ActivityPub
Port 9110db41c5 to glitch
			
			
This commit is contained in:
		| @@ -117,14 +117,15 @@ export function refreshTimeline(timelineId, path, params = {}) { | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| export const refreshHomeTimeline         = () => refreshTimeline('home', '/api/v1/timelines/home'); | ||||
| export const refreshPublicTimeline       = () => refreshTimeline('public', '/api/v1/timelines/public'); | ||||
| export const refreshCommunityTimeline    = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true }); | ||||
| export const refreshDirectTimeline       = () => refreshTimeline('direct', '/api/v1/timelines/direct'); | ||||
| export const refreshAccountTimeline      = (accountId, withReplies) => refreshTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies }); | ||||
| export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true }); | ||||
| export const refreshHashtagTimeline      = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`); | ||||
| export const refreshListTimeline         = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`); | ||||
| export const refreshHomeTimeline            = () => refreshTimeline('home', '/api/v1/timelines/home'); | ||||
| export const refreshPublicTimeline          = () => refreshTimeline('public', '/api/v1/timelines/public'); | ||||
| export const refreshCommunityTimeline       = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true }); | ||||
| export const refreshDirectTimeline          = () => refreshTimeline('direct', '/api/v1/timelines/direct'); | ||||
| export const refreshAccountTimeline         = (accountId, withReplies) => refreshTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies }); | ||||
| export const refreshAccountFeaturedTimeline = accountId => refreshTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true }); | ||||
| export const refreshAccountMediaTimeline    = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true }); | ||||
| export const refreshHashtagTimeline         = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`); | ||||
| export const refreshListTimeline            = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`); | ||||
|  | ||||
| export function refreshTimelineFail(timeline, error, skipLoading) { | ||||
|   return { | ||||
|   | ||||
| @@ -11,6 +11,7 @@ export default class StatusList extends ImmutablePureComponent { | ||||
|   static propTypes = { | ||||
|     scrollKey: PropTypes.string.isRequired, | ||||
|     statusIds: ImmutablePropTypes.list.isRequired, | ||||
|     featuredStatusIds: ImmutablePropTypes.list, | ||||
|     onScrollToBottom: PropTypes.func, | ||||
|     onScrollToTop: PropTypes.func, | ||||
|     onScroll: PropTypes.func, | ||||
| @@ -50,7 +51,7 @@ export default class StatusList extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { statusIds, ...other }  = this.props; | ||||
|     const { statusIds, featuredStatusIds, ...other }  = this.props; | ||||
|     const { isLoading, isPartial } = other; | ||||
|  | ||||
|     if (isPartial) { | ||||
| @@ -68,8 +69,8 @@ export default class StatusList extends ImmutablePureComponent { | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     const scrollableContent = (isLoading || statusIds.size > 0) ? ( | ||||
|       statusIds.map((statusId) => ( | ||||
|     let scrollableContent = (isLoading || statusIds.size > 0) ? ( | ||||
|       statusIds.map(statusId => ( | ||||
|         <StatusContainer | ||||
|           key={statusId} | ||||
|           id={statusId} | ||||
| @@ -79,6 +80,18 @@ export default class StatusList extends ImmutablePureComponent { | ||||
|       )) | ||||
|     ) : null; | ||||
|  | ||||
|     if (scrollableContent && featuredStatusIds) { | ||||
|       scrollableContent = featuredStatusIds.map(statusId => ( | ||||
|         <StatusContainer | ||||
|           key={`f-${statusId}`} | ||||
|           id={statusId} | ||||
|           featured | ||||
|           onMoveUp={this.handleMoveUp} | ||||
|           onMoveDown={this.handleMoveDown} | ||||
|         /> | ||||
|       )).concat(scrollableContent); | ||||
|     } | ||||
|  | ||||
|     return ( | ||||
|       <ScrollableList {...other} ref={this.setRef}> | ||||
|         {scrollableContent} | ||||
|   | ||||
| @@ -34,6 +34,10 @@ export default class StatusPrepend extends React.PureComponent { | ||||
|       </a> | ||||
|     ); | ||||
|     switch (type) { | ||||
|     case 'featured': | ||||
|       return ( | ||||
|         <FormattedMessage id='status.pinned' defaultMessage='Pinned toot' /> | ||||
|       ); | ||||
|     case 'reblogged_by': | ||||
|       return ( | ||||
|         <FormattedMessage | ||||
| @@ -67,11 +71,11 @@ export default class StatusPrepend extends React.PureComponent { | ||||
|     const { type } = this.props; | ||||
|  | ||||
|     return !type ? null : ( | ||||
|       <aside className={type === 'reblogged_by' ? 'status__prepend' : 'notification__message'}> | ||||
|         <div className={type === 'reblogged_by' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}> | ||||
|       <aside className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend' : 'notification__message'}> | ||||
|         <div className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}> | ||||
|           <i | ||||
|             className={`fa fa-fw fa-${ | ||||
|               type === 'favourite' ? 'star star-icon' : 'retweet' | ||||
|               type === 'favourite' ? 'star star-icon' : (type === 'featured' ? 'thumb-tack' : 'retweet') | ||||
|             } status__prepend-icon`} | ||||
|           /> | ||||
|         </div> | ||||
|   | ||||
| @@ -38,7 +38,10 @@ const makeMapStateToProps = () => { | ||||
|     let account = undefined; | ||||
|     let prepend = undefined; | ||||
|  | ||||
|     if (reblogStatus !== null && typeof reblogStatus === 'object') { | ||||
|     if (props.featured) { | ||||
|       account = status.get('account'); | ||||
|       prepend = 'featured'; | ||||
|     } else if (reblogStatus !== null && typeof reblogStatus === 'object') { | ||||
|       account = status.get('account'); | ||||
|       status = reblogStatus; | ||||
|       prepend = 'reblogged_by'; | ||||
|   | ||||
| @@ -20,6 +20,7 @@ export default class Header extends ImmutablePureComponent { | ||||
|     onMute: PropTypes.func.isRequired, | ||||
|     onBlockDomain: PropTypes.func.isRequired, | ||||
|     onUnblockDomain: PropTypes.func.isRequired, | ||||
|     hideTabs: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   static contextTypes = { | ||||
| @@ -67,7 +68,7 @@ export default class Header extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { account } = this.props; | ||||
|     const { account, hideTabs } = this.props; | ||||
|  | ||||
|     if (account === null) { | ||||
|       return <MissingIndicator />; | ||||
| @@ -92,11 +93,13 @@ export default class Header extends ImmutablePureComponent { | ||||
|           onUnblockDomain={this.handleUnblockDomain} | ||||
|         /> | ||||
|  | ||||
|         <div className='account__section-headline'> | ||||
|           <NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink> | ||||
|           <NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink> | ||||
|           <NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink> | ||||
|         </div> | ||||
|         {!hideTabs && ( | ||||
|           <div className='account__section-headline'> | ||||
|             <NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink> | ||||
|             <NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink> | ||||
|             <NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink> | ||||
|           </div> | ||||
|         )} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { fetchAccount } from 'flavours/glitch/actions/accounts'; | ||||
| import { refreshAccountTimeline, expandAccountTimeline } from 'flavours/glitch/actions/timelines'; | ||||
| import { refreshAccountTimeline, refreshAccountFeaturedTimeline, expandAccountTimeline } from 'flavours/glitch/actions/timelines'; | ||||
| import StatusList from '../../components/status_list'; | ||||
| import LoadingIndicator from '../../components/loading_indicator'; | ||||
| import Column from '../ui/components/column'; | ||||
| @@ -17,6 +17,7 @@ const mapStateToProps = (state, { params: { accountId }, withReplies = false }) | ||||
|  | ||||
|   return { | ||||
|     statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()), | ||||
|     featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()), | ||||
|     isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']), | ||||
|     hasMore: !!state.getIn(['timelines', `account:${path}`, 'next']), | ||||
|   }; | ||||
| @@ -29,19 +30,24 @@ export default class AccountTimeline extends ImmutablePureComponent { | ||||
|     params: PropTypes.object.isRequired, | ||||
|     dispatch: PropTypes.func.isRequired, | ||||
|     statusIds: ImmutablePropTypes.list, | ||||
|     featuredStatusIds: ImmutablePropTypes.list, | ||||
|     isLoading: PropTypes.bool, | ||||
|     hasMore: PropTypes.bool, | ||||
|     withReplies: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
|     this.props.dispatch(fetchAccount(this.props.params.accountId)); | ||||
|     this.props.dispatch(refreshAccountTimeline(this.props.params.accountId, this.props.withReplies)); | ||||
|     const { params: { accountId }, withReplies } = this.props; | ||||
|  | ||||
|     this.props.dispatch(fetchAccount(accountId)); | ||||
|     this.props.dispatch(refreshAccountFeaturedTimeline(accountId)); | ||||
|     this.props.dispatch(refreshAccountTimeline(accountId, withReplies)); | ||||
|   } | ||||
|  | ||||
|   componentWillReceiveProps (nextProps) { | ||||
|     if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) { | ||||
|       this.props.dispatch(fetchAccount(nextProps.params.accountId)); | ||||
|       this.props.dispatch(refreshAccountFeaturedTimeline(nextProps.params.accountId)); | ||||
|       this.props.dispatch(refreshAccountTimeline(nextProps.params.accountId, nextProps.params.withReplies)); | ||||
|     } | ||||
|   } | ||||
| @@ -53,7 +59,7 @@ export default class AccountTimeline extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { statusIds, isLoading, hasMore } = this.props; | ||||
|     const { statusIds, featuredStatusIds, isLoading, hasMore } = this.props; | ||||
|  | ||||
|     if (!statusIds && isLoading) { | ||||
|       return ( | ||||
| @@ -71,6 +77,7 @@ export default class AccountTimeline extends ImmutablePureComponent { | ||||
|           prepend={<HeaderContainer accountId={this.props.params.accountId} />} | ||||
|           scrollKey='account_timeline' | ||||
|           statusIds={statusIds} | ||||
|           featuredStatusIds={featuredStatusIds} | ||||
|           isLoading={isLoading} | ||||
|           hasMore={hasMore} | ||||
|           onScrollToBottom={this.handleScrollToBottom} | ||||
|   | ||||
| @@ -80,7 +80,7 @@ export default class Followers extends ImmutablePureComponent { | ||||
|         <ScrollContainer scrollKey='followers'> | ||||
|           <div className='scrollable' onScroll={this.handleScroll}> | ||||
|             <div className='followers'> | ||||
|               <HeaderContainer accountId={this.props.params.accountId} /> | ||||
|               <HeaderContainer accountId={this.props.params.accountId} hideTabs /> | ||||
|               {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)} | ||||
|               {loadMore} | ||||
|             </div> | ||||
|   | ||||
| @@ -80,7 +80,7 @@ export default class Following extends ImmutablePureComponent { | ||||
|         <ScrollContainer scrollKey='following'> | ||||
|           <div className='scrollable' onScroll={this.handleScroll}> | ||||
|             <div className='following'> | ||||
|               <HeaderContainer accountId={this.props.params.accountId} /> | ||||
|               <HeaderContainer accountId={this.props.params.accountId} hideTabs /> | ||||
|               {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)} | ||||
|               {loadMore} | ||||
|             </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user