Adding hashtags
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import api from '../api'
|
||||
import Immutable from 'immutable';
|
||||
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||
@ -54,20 +55,25 @@ export function refreshTimelineRequest(timeline) {
|
||||
};
|
||||
};
|
||||
|
||||
export function refreshTimeline(timeline, replace = false) {
|
||||
export function refreshTimeline(timeline, replace = false, id = null) {
|
||||
return function (dispatch, getState) {
|
||||
dispatch(refreshTimelineRequest(timeline));
|
||||
|
||||
const ids = getState().getIn(['timelines', timeline]);
|
||||
const ids = getState().getIn(['timelines', timeline], Immutable.List());
|
||||
const newestId = ids.size > 0 ? ids.first() : null;
|
||||
|
||||
let params = '';
|
||||
let path = timeline;
|
||||
|
||||
if (newestId !== null && !replace) {
|
||||
params = `?since_id=${newestId}`;
|
||||
}
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${timeline}${params}`).then(function (response) {
|
||||
if (id) {
|
||||
path = `${path}/${id}`
|
||||
}
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${path}${params}`).then(function (response) {
|
||||
dispatch(refreshTimelineSuccess(timeline, response.data, replace));
|
||||
}).catch(function (error) {
|
||||
dispatch(refreshTimelineFail(timeline, error));
|
||||
@ -83,13 +89,19 @@ export function refreshTimelineFail(timeline, error) {
|
||||
};
|
||||
};
|
||||
|
||||
export function expandTimeline(timeline) {
|
||||
export function expandTimeline(timeline, id = null) {
|
||||
return (dispatch, getState) => {
|
||||
const lastId = getState().getIn(['timelines', timeline]).last();
|
||||
const lastId = getState().getIn(['timelines', timeline], Immutable.List()).last();
|
||||
|
||||
dispatch(expandTimelineRequest(timeline));
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${timeline}?max_id=${lastId}`).then(response => {
|
||||
let path = timeline;
|
||||
|
||||
if (id) {
|
||||
path = `${path}/${id}`
|
||||
}
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${path}?max_id=${lastId}`).then(response => {
|
||||
dispatch(expandTimelineSuccess(timeline, response.data));
|
||||
}).catch(error => {
|
||||
dispatch(expandTimelineFail(timeline, error));
|
||||
|
@ -23,11 +23,14 @@ const StatusContent = React.createClass({
|
||||
|
||||
if (mention) {
|
||||
link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
|
||||
} else if (link.text[0] === '#' || (link.previousSibling && link.previousSibling.text === '#')) {
|
||||
link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
|
||||
} else {
|
||||
link.setAttribute('target', '_blank');
|
||||
link.setAttribute('rel', 'noopener');
|
||||
link.addEventListener('click', this.onNormalClick, false);
|
||||
}
|
||||
|
||||
link.addEventListener('click', this.onNormalClick, false);
|
||||
}
|
||||
},
|
||||
|
||||
@ -36,8 +39,15 @@ const StatusContent = React.createClass({
|
||||
e.preventDefault();
|
||||
this.context.router.push(`/accounts/${mention.get('id')}`);
|
||||
}
|
||||
},
|
||||
|
||||
e.stopPropagation();
|
||||
onHashtagClick (hashtag, e) {
|
||||
hashtag = hashtag.replace(/^#/, '').toLowerCase();
|
||||
|
||||
if (e.button === 0) {
|
||||
e.preventDefault();
|
||||
this.context.router.push(`/statuses/tag/${hashtag}`);
|
||||
}
|
||||
},
|
||||
|
||||
onNormalClick (e) {
|
||||
|
@ -30,6 +30,7 @@ import Followers from '../features/followers';
|
||||
import Following from '../features/following';
|
||||
import Reblogs from '../features/reblogs';
|
||||
import Favourites from '../features/favourites';
|
||||
import HashtagTimeline from '../features/hashtag_timeline';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
@ -85,6 +86,7 @@ const Mastodon = React.createClass({
|
||||
<Route path='/statuses/home' component={HomeTimeline} />
|
||||
<Route path='/statuses/mentions' component={MentionsTimeline} />
|
||||
<Route path='/statuses/all' component={PublicTimeline} />
|
||||
<Route path='/statuses/tag/:id' component={HashtagTimeline} />
|
||||
|
||||
<Route path='/statuses/:statusId' component={Status} />
|
||||
<Route path='/statuses/:statusId/reblogs' component={Reblogs} />
|
||||
|
@ -47,7 +47,7 @@ const Account = React.createClass({
|
||||
this.props.dispatch(fetchAccount(Number(this.props.params.accountId)));
|
||||
},
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
|
||||
this.props.dispatch(fetchAccount(Number(nextProps.params.accountId)));
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
import { connect } from 'react-redux';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import StatusListContainer from '../ui/containers/status_list_container';
|
||||
import Column from '../ui/components/column';
|
||||
import {
|
||||
refreshTimeline,
|
||||
updateTimeline
|
||||
} from '../../actions/timelines';
|
||||
|
||||
const HashtagTimeline = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
params: React.PropTypes.object.isRequired,
|
||||
dispatch: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
_subscribe (dispatch, id) {
|
||||
if (typeof App !== 'undefined') {
|
||||
this.subscription = App.cable.subscriptions.create({
|
||||
channel: 'HashtagChannel',
|
||||
tag: id
|
||||
}, {
|
||||
|
||||
received (data) {
|
||||
dispatch(updateTimeline('tag', JSON.parse(data.message)));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_unsubscribe () {
|
||||
if (typeof this.subscription !== 'undefined') {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
|
||||
dispatch(refreshTimeline('tag', true, id));
|
||||
this._subscribe(dispatch, id);
|
||||
},
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.params.id !== this.props.params.id) {
|
||||
this.props.dispatch(refreshTimeline('tag', true, nextProps.params.id));
|
||||
this._unsubscribe();
|
||||
this._subscribe(this.props.dispatch, nextProps.params.id);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount () {
|
||||
this._unsubscribe();
|
||||
},
|
||||
|
||||
render () {
|
||||
const { id } = this.props.params;
|
||||
|
||||
return (
|
||||
<Column icon='hashtag' heading={id}>
|
||||
<StatusListContainer type='tag' id={id} />
|
||||
</Column>
|
||||
);
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default connect()(HashtagTimeline);
|
@ -1,15 +1,16 @@
|
||||
import { connect } from 'react-redux';
|
||||
import StatusList from '../../../components/status_list';
|
||||
import { expandTimeline } from '../../../actions/timelines';
|
||||
import { connect } from 'react-redux';
|
||||
import StatusList from '../../../components/status_list';
|
||||
import { expandTimeline } from '../../../actions/timelines';
|
||||
import Immutable from 'immutable';
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
statusIds: state.getIn(['timelines', props.type])
|
||||
statusIds: state.getIn(['timelines', props.type], Immutable.List())
|
||||
});
|
||||
|
||||
const mapDispatchToProps = function (dispatch, props) {
|
||||
return {
|
||||
onScrollToBottom () {
|
||||
dispatch(expandTimeline(props.type));
|
||||
dispatch(expandTimeline(props.type, props.id));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -25,6 +25,7 @@ const initialState = Immutable.Map({
|
||||
home: Immutable.List(),
|
||||
mentions: Immutable.List(),
|
||||
public: Immutable.List(),
|
||||
tag: Immutable.List(),
|
||||
accounts_timelines: Immutable.Map(),
|
||||
ancestors: Immutable.Map(),
|
||||
descendants: Immutable.Map()
|
||||
@ -55,7 +56,7 @@ const normalizeTimeline = (state, timeline, statuses, replace = false) => {
|
||||
ids = ids.set(i, status.get('id'));
|
||||
});
|
||||
|
||||
return state.update(timeline, list => (replace ? ids : list.unshift(...ids)));
|
||||
return state.update(timeline, Immutable.List(), list => (replace ? ids : list.unshift(...ids)));
|
||||
};
|
||||
|
||||
const appendNormalizedTimeline = (state, timeline, statuses) => {
|
||||
@ -66,7 +67,7 @@ const appendNormalizedTimeline = (state, timeline, statuses) => {
|
||||
moreIds = moreIds.set(i, status.get('id'));
|
||||
});
|
||||
|
||||
return state.update(timeline, list => list.push(...moreIds));
|
||||
return state.update(timeline, Immutable.List(), list => list.push(...moreIds));
|
||||
};
|
||||
|
||||
const normalizeAccountTimeline = (state, accountId, statuses, replace = false) => {
|
||||
@ -94,7 +95,7 @@ const appendNormalizedAccountTimeline = (state, accountId, statuses) => {
|
||||
const updateTimeline = (state, timeline, status, references) => {
|
||||
state = normalizeStatus(state, status);
|
||||
|
||||
state = state.update(timeline, list => {
|
||||
state = state.update(timeline, Immutable.List(), list => {
|
||||
if (list.includes(status.get('id'))) {
|
||||
return list;
|
||||
}
|
||||
@ -113,7 +114,7 @@ const updateTimeline = (state, timeline, status, references) => {
|
||||
|
||||
const deleteStatus = (state, id, accountId, references) => {
|
||||
// Remove references from timelines
|
||||
['home', 'mentions', 'public'].forEach(function (timeline) {
|
||||
['home', 'mentions', 'public', 'tag'].forEach(function (timeline) {
|
||||
state = state.update(timeline, list => list.filterNot(item => item === id));
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user