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