@ -0,0 +1,83 @@ | |||
import api, { getLinks } from '../api' | |||
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST'; | |||
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS'; | |||
export const FAVOURITED_STATUSES_FETCH_FAIL = 'FAVOURITED_STATUSES_FETCH_FAIL'; | |||
export const FAVOURITED_STATUSES_EXPAND_REQUEST = 'FAVOURITED_STATUSES_EXPAND_REQUEST'; | |||
export const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS'; | |||
export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FAIL'; | |||
export function fetchFavouritedStatuses() { | |||
return (dispatch, getState) => { | |||
dispatch(fetchFavouritedStatusesRequest()); | |||
api(getState).get('/api/v1/favourites').then(response => { | |||
const next = getLinks(response).refs.find(link => link.rel === 'next'); | |||
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null)); | |||
}).catch(error => { | |||
dispatch(fetchFavouritedStatusesFail(error)); | |||
}); | |||
}; | |||
}; | |||
export function fetchFavouritedStatusesRequest() { | |||
return { | |||
type: FAVOURITED_STATUSES_FETCH_REQUEST | |||
}; | |||
}; | |||
export function fetchFavouritedStatusesSuccess(statuses, next) { | |||
return { | |||
type: FAVOURITED_STATUSES_FETCH_SUCCESS, | |||
statuses, | |||
next | |||
}; | |||
}; | |||
export function fetchFavouritedStatusesFail(error) { | |||
return { | |||
type: FAVOURITED_STATUSES_FETCH_FAIL, | |||
error | |||
}; | |||
}; | |||
export function expandFavouritedStatuses() { | |||
return (dispatch, getState) => { | |||
const url = getState().getIn(['status_lists', 'favourites', 'next'], null); | |||
if (url === null) { | |||
return; | |||
} | |||
dispatch(expandFavouritedStatusesRequest()); | |||
api(getState).get(url).then(response => { | |||
const next = getLinks(response).refs.find(link => link.rel === 'next'); | |||
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null)); | |||
}).catch(error => { | |||
dispatch(expandFavouritedStatusesFail(error)); | |||
}); | |||
}; | |||
}; | |||
export function expandFavouritedStatusesRequest() { | |||
return { | |||
type: FAVOURITED_STATUSES_EXPAND_REQUEST | |||
}; | |||
}; | |||
export function expandFavouritedStatusesSuccess(statuses, next) { | |||
return { | |||
type: FAVOURITED_STATUSES_EXPAND_SUCCESS, | |||
statuses, | |||
next | |||
}; | |||
}; | |||
export function expandFavouritedStatusesFail(error) { | |||
return { | |||
type: FAVOURITED_STATUSES_EXPAND_FAIL, | |||
error | |||
}; | |||
}; |
@ -0,0 +1,63 @@ | |||
import { connect } from 'react-redux'; | |||
import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import LoadingIndicator from '../../components/loading_indicator'; | |||
import { fetchFavouritedStatuses, expandFavouritedStatuses } from '../../actions/favourites'; | |||
import Column from '../ui/components/column'; | |||
import StatusList from '../../components/status_list'; | |||
import ColumnBackButton from '../public_timeline/components/column_back_button'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
heading: { id: 'column.favourites', defaultMessage: 'Favourites' } | |||
}); | |||
const mapStateToProps = state => ({ | |||
statusIds: state.getIn(['status_lists', 'favourites', 'items']), | |||
loaded: state.getIn(['status_lists', 'favourites', 'loaded']), | |||
me: state.getIn(['meta', 'me']) | |||
}); | |||
const Favourites = React.createClass({ | |||
propTypes: { | |||
params: React.PropTypes.object.isRequired, | |||
dispatch: React.PropTypes.func.isRequired, | |||
statusIds: ImmutablePropTypes.list.isRequired, | |||
loaded: React.PropTypes.bool, | |||
intl: React.PropTypes.object.isRequired, | |||
me: React.PropTypes.number.isRequired | |||
}, | |||
mixins: [PureRenderMixin], | |||
componentWillMount () { | |||
this.props.dispatch(fetchFavouritedStatuses()); | |||
}, | |||
handleScrollToBottom () { | |||
this.props.dispatch(expandFavouritedStatuses()); | |||
}, | |||
render () { | |||
const { statusIds, loaded, intl, me } = this.props; | |||
if (!loaded) { | |||
return ( | |||
<Column> | |||
<LoadingIndicator /> | |||
</Column> | |||
); | |||
} | |||
return ( | |||
<Column icon='star' heading={intl.formatMessage(messages.heading)}> | |||
<ColumnBackButton /> | |||
<StatusList statusIds={statusIds} me={me} onScrollToBottom={this.handleScrollToBottom} /> | |||
</Column> | |||
); | |||
} | |||
}); | |||
export default connect(mapStateToProps)(injectIntl(Favourites)); |
@ -0,0 +1,25 @@ | |||
import { showLoading, hideLoading } from 'react-redux-loading-bar'; | |||
const defaultTypeSuffixes = ['PENDING', 'FULFILLED', 'REJECTED']; | |||
export default function loadingBarMiddleware(config = {}) { | |||
const promiseTypeSuffixes = config.promiseTypeSuffixes || defaultTypeSuffixes; | |||
return ({ dispatch }) => next => (action) => { | |||
if (action.type && !action.skipLoading) { | |||
const [PENDING, FULFILLED, REJECTED] = promiseTypeSuffixes; | |||
const isPending = new RegExp(`${PENDING}$`, 'g'); | |||
const isFulfilled = new RegExp(`${FULFILLED}$`, 'g'); | |||
const isRejected = new RegExp(`${REJECTED}$`, 'g'); | |||
if (action.type.match(isPending)) { | |||
dispatch(showLoading()); | |||
} else if (action.type.match(isFulfilled) || action.type.match(isRejected)) { | |||
dispatch(hideLoading()); | |||
} | |||
} | |||
return next(action); | |||
}; | |||
}; |
@ -0,0 +1,39 @@ | |||
import { | |||
FAVOURITED_STATUSES_FETCH_SUCCESS, | |||
FAVOURITED_STATUSES_EXPAND_SUCCESS | |||
} from '../actions/favourites'; | |||
import Immutable from 'immutable'; | |||
const initialState = Immutable.Map({ | |||
favourites: Immutable.Map({ | |||
next: null, | |||
loaded: false, | |||
items: Immutable.List() | |||
}) | |||
}); | |||
const normalizeList = (state, listType, statuses, next) => { | |||
return state.update(listType, listMap => listMap.withMutations(map => { | |||
map.set('next', next); | |||
map.set('loaded', true); | |||
map.set('items', Immutable.List(statuses.map(item => item.id))); | |||
})); | |||
}; | |||
const appendToList = (state, listType, statuses, next) => { | |||
return state.update(listType, listMap => listMap.withMutations(map => { | |||
map.set('next', next); | |||
map.set('items', map.get('items').push(...statuses.map(item => item.id))); | |||
})); | |||
}; | |||
export default function statusLists(state = initialState, action) { | |||
switch(action.type) { | |||
case FAVOURITED_STATUSES_FETCH_SUCCESS: | |||
return normalizeList(state, 'favourites', action.statuses, action.next); | |||
case FAVOURITED_STATUSES_EXPAND_SUCCESS: | |||
return appendToList(state, 'favourites', action.statuses, action.next); | |||
default: | |||
return state; | |||
} | |||
}; |