Unlike boosts and like, there is no confirmation dialog as misclicking can be recovered without another user seeing it.closed-social-glitch-2
@ -0,0 +1,87 @@ | |||
import api, { getLinks } from 'flavours/glitch/util/api'; | |||
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST'; | |||
export const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS'; | |||
export const BOOKMARKED_STATUSES_FETCH_FAIL = 'BOOKMARKED_STATUSES_FETCH_FAIL'; | |||
export const BOOKMARKED_STATUSES_EXPAND_REQUEST = 'BOOKMARKED_STATUSES_EXPAND_REQUEST'; | |||
export const BOOKMARKED_STATUSES_EXPAND_SUCCESS = 'BOOKMARKED_STATUSES_EXPAND_SUCCESS'; | |||
export const BOOKMARKED_STATUSES_EXPAND_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL'; | |||
export function fetchBookmarkedStatuses() { | |||
return (dispatch, getState) => { | |||
if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) { | |||
return; | |||
} | |||
dispatch(fetchBookmarkedStatusesRequest()); | |||
api(getState).get('/api/v1/bookmarks').then(response => { | |||
const next = getLinks(response).refs.find(link => link.rel === 'next'); | |||
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); | |||
}).catch(error => { | |||
dispatch(fetchBookmarkedStatusesFail(error)); | |||
}); | |||
}; | |||
}; | |||
export function fetchBookmarkedStatusesRequest() { | |||
return { | |||
type: BOOKMARKED_STATUSES_FETCH_REQUEST, | |||
}; | |||
}; | |||
export function fetchBookmarkedStatusesSuccess(statuses, next) { | |||
return { | |||
type: BOOKMARKED_STATUSES_FETCH_SUCCESS, | |||
statuses, | |||
next, | |||
}; | |||
}; | |||
export function fetchBookmarkedStatusesFail(error) { | |||
return { | |||
type: BOOKMARKED_STATUSES_FETCH_FAIL, | |||
error, | |||
}; | |||
}; | |||
export function expandBookmarkedStatuses() { | |||
return (dispatch, getState) => { | |||
const url = getState().getIn(['status_lists', 'bookmarks', 'next'], null); | |||
if (url === null || getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) { | |||
return; | |||
} | |||
dispatch(expandBookmarkedStatusesRequest()); | |||
api(getState).get(url).then(response => { | |||
const next = getLinks(response).refs.find(link => link.rel === 'next'); | |||
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); | |||
}).catch(error => { | |||
dispatch(expandBookmarkedStatusesFail(error)); | |||
}); | |||
}; | |||
}; | |||
export function expandBookmarkedStatusesRequest() { | |||
return { | |||
type: BOOKMARKED_STATUSES_EXPAND_REQUEST, | |||
}; | |||
}; | |||
export function expandBookmarkedStatusesSuccess(statuses, next) { | |||
return { | |||
type: BOOKMARKED_STATUSES_EXPAND_SUCCESS, | |||
statuses, | |||
next, | |||
}; | |||
}; | |||
export function expandBookmarkedStatusesFail(error) { | |||
return { | |||
type: BOOKMARKED_STATUSES_EXPAND_FAIL, | |||
error, | |||
}; | |||
}; |
@ -0,0 +1,98 @@ | |||
import React from 'react'; | |||
import { connect } from 'react-redux'; | |||
import PropTypes from 'prop-types'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'flavours/glitch/actions/bookmarks'; | |||
import Column from 'flavours/glitch/features/ui/components/column'; | |||
import ColumnHeader from 'flavours/glitch/components/column_header'; | |||
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns'; | |||
import StatusList from 'flavours/glitch/components/status_list'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
import ImmutablePureComponent from 'react-immutable-pure-component'; | |||
import { debounce } from 'lodash'; | |||
const messages = defineMessages({ | |||
heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' }, | |||
}); | |||
const mapStateToProps = state => ({ | |||
statusIds: state.getIn(['status_lists', 'bookmarks', 'items']), | |||
isLoading: state.getIn(['status_lists', 'bookmarks', 'isLoading'], true), | |||
hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']), | |||
}); | |||
@connect(mapStateToProps) | |||
@injectIntl | |||
export default class Bookmarks extends ImmutablePureComponent { | |||
static propTypes = { | |||
dispatch: PropTypes.func.isRequired, | |||
statusIds: ImmutablePropTypes.list.isRequired, | |||
intl: PropTypes.object.isRequired, | |||
columnId: PropTypes.string, | |||
multiColumn: PropTypes.bool, | |||
hasMore: PropTypes.bool, | |||
isLoading: PropTypes.bool, | |||
}; | |||
componentWillMount () { | |||
this.props.dispatch(fetchBookmarkedStatuses()); | |||
} | |||
handlePin = () => { | |||
const { columnId, dispatch } = this.props; | |||
if (columnId) { | |||
dispatch(removeColumn(columnId)); | |||
} else { | |||
dispatch(addColumn('BOOKMARKS', {})); | |||
} | |||
} | |||
handleMove = (dir) => { | |||
const { columnId, dispatch } = this.props; | |||
dispatch(moveColumn(columnId, dir)); | |||
} | |||
handleHeaderClick = () => { | |||
this.column.scrollTop(); | |||
} | |||
setRef = c => { | |||
this.column = c; | |||
} | |||
handleScrollToBottom = debounce(() => { | |||
this.props.dispatch(expandBookmarkedStatuses()); | |||
}, 300, { leading: true }) | |||
render () { | |||
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; | |||
const pinned = !!columnId; | |||
return ( | |||
<Column ref={this.setRef} name='bookmarks'> | |||
<ColumnHeader | |||
icon='bookmark' | |||
title={intl.formatMessage(messages.heading)} | |||
onPin={this.handlePin} | |||
onMove={this.handleMove} | |||
onClick={this.handleHeaderClick} | |||
pinned={pinned} | |||
multiColumn={multiColumn} | |||
showBackButton | |||
/> | |||
<StatusList | |||
trackScroll={!pinned} | |||
statusIds={statusIds} | |||
scrollKey={`bookmarked_statuses-${columnId}`} | |||
hasMore={hasMore} | |||
isLoading={isLoading} | |||
onScrollToBottom={this.handleScrollToBottom} | |||
/> | |||
</Column> | |||
); | |||
} | |||
} |