ScheduledStatuses: scaffolding
This commit is contained in:
parent
f64f39eb19
commit
5f9d95301c
6 changed files with 230 additions and 0 deletions
87
app/soapbox/actions/scheduled_statuses.js
Normal file
87
app/soapbox/actions/scheduled_statuses.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
import api, { getLinks } from '../api';
|
||||
|
||||
export const SCHEDULED_STATUSES_FETCH_REQUEST = 'SCHEDULED_STATUSES_FETCH_REQUEST';
|
||||
export const SCHEDULED_STATUSES_FETCH_SUCCESS = 'SCHEDULED_STATUSES_FETCH_SUCCESS';
|
||||
export const SCHEDULED_STATUSES_FETCH_FAIL = 'SCHEDULED_STATUSES_FETCH_FAIL';
|
||||
|
||||
export const SCHEDULED_STATUSES_EXPAND_REQUEST = 'SCHEDULED_STATUSES_EXPAND_REQUEST';
|
||||
export const SCHEDULED_STATUSES_EXPAND_SUCCESS = 'SCHEDULED_STATUSES_EXPAND_SUCCESS';
|
||||
export const SCHEDULED_STATUSES_EXPAND_FAIL = 'SCHEDULED_STATUSES_EXPAND_FAIL';
|
||||
|
||||
export function fetchScheduledStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
if (getState().getIn(['status_lists', 'scheduled_statuses', 'isLoading'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchScheduledStatusesRequest());
|
||||
|
||||
api(getState).get('/api/v1/scheduled_statuses').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(fetchScheduledStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => {
|
||||
dispatch(fetchScheduledStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchScheduledStatusesRequest() {
|
||||
return {
|
||||
type: SCHEDULED_STATUSES_FETCH_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchScheduledStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: SCHEDULED_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchScheduledStatusesFail(error) {
|
||||
return {
|
||||
type: SCHEDULED_STATUSES_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandScheduledStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
const url = getState().getIn(['status_lists', 'scheduled_statuses', 'next'], null);
|
||||
|
||||
if (url === null || getState().getIn(['status_lists', 'scheduled_statuses', 'isLoading'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandScheduledStatusesRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(expandScheduledStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => {
|
||||
dispatch(expandScheduledStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function expandScheduledStatusesRequest() {
|
||||
return {
|
||||
type: SCHEDULED_STATUSES_EXPAND_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandScheduledStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: SCHEDULED_STATUSES_EXPAND_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandScheduledStatusesFail(error) {
|
||||
return {
|
||||
type: SCHEDULED_STATUSES_EXPAND_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
74
app/soapbox/features/scheduled_statuses/index.js
Normal file
74
app/soapbox/features/scheduled_statuses/index.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Column from '../ui/components/column';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import StatusList from '../../components/status_list';
|
||||
import { fetchScheduledStatuses, expandScheduledStatuses } from '../../actions/scheduled_statuses';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.scheduled_statuses', defaultMessage: 'Scheduled Statuses' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
statusIds: state.getIn(['status_lists', 'scheduled_statuses', 'items']),
|
||||
isLoading: state.getIn(['status_lists', 'scheduled_statuses', 'isLoading'], true),
|
||||
hasMore: !!state.getIn(['status_lists', 'scheduled_statuses', 'next']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class ScheduledStatuses extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
statusIds: ImmutablePropTypes.list.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
multiColumn: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(fetchScheduledStatuses());
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
this.props.dispatch(expandScheduledStatuses());
|
||||
}, 300, { leading: true })
|
||||
|
||||
|
||||
render() {
|
||||
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
||||
const pinned = !!columnId;
|
||||
|
||||
const emptyMessage = <FormattedMessage id='empty_column.scheduled_statuses' defaultMessage="You don't have any scheduled statuses yet. When you add one, it will show up here." />;
|
||||
|
||||
return (
|
||||
<Column icon='edit' heading={intl.formatMessage(messages.heading)} backBtnSlim>
|
||||
<StatusList
|
||||
trackScroll={!pinned}
|
||||
statusIds={statusIds}
|
||||
scrollKey={`scheduled_statuses-${columnId}`}
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -100,6 +100,7 @@ import {
|
|||
Reports,
|
||||
ModerationLog,
|
||||
CryptoDonate,
|
||||
ScheduledStatuses,
|
||||
} from './util/async-components';
|
||||
|
||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||
|
@ -300,6 +301,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
|||
<WrappedRoute path='/@:username/posts/:statusId/reblogs' layout={LAYOUT.DEFAULT} component={Reblogs} content={children} />
|
||||
|
||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/scheduled_statuses' layout={LAYOUT.DEFAULT} component={ScheduledStatuses} content={children} />
|
||||
|
||||
<Redirect exact from='/settings' to='/settings/preferences' />
|
||||
<WrappedRoute path='/settings/preferences' layout={LAYOUT.DEFAULT} component={Preferences} content={children} />
|
||||
|
|
|
@ -233,3 +233,7 @@ export function ModerationLog() {
|
|||
export function CryptoDonate() {
|
||||
return import(/* webpackChunkName: "features/crypto_donate" */'../../crypto_donate');
|
||||
}
|
||||
|
||||
export function ScheduledStatuses() {
|
||||
return import(/* webpackChunkName: "features/scheduled_statuses" */'../../scheduled_statuses');
|
||||
}
|
||||
|
|
45
app/soapbox/reducers/scheduled_statuses.js
Normal file
45
app/soapbox/reducers/scheduled_statuses.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||
|
||||
const importStatuses = (state, statuses) =>
|
||||
state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status)));
|
||||
|
||||
const deleteStatus = (state, id, references) => {
|
||||
references.forEach(ref => {
|
||||
state = deleteStatus(state, ref[0], []);
|
||||
});
|
||||
|
||||
return state.delete(id);
|
||||
};
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function statuses(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case STATUS_IMPORT:
|
||||
return importStatus(state, action.status);
|
||||
case STATUSES_IMPORT:
|
||||
return importStatuses(state, action.statuses);
|
||||
case STATUS_REVEAL:
|
||||
return state.withMutations(map => {
|
||||
action.ids.forEach(id => {
|
||||
if (!(state.get(id) === undefined)) {
|
||||
map.setIn([id, 'hidden'], false);
|
||||
}
|
||||
});
|
||||
});
|
||||
case STATUS_HIDE:
|
||||
return state.withMutations(map => {
|
||||
action.ids.forEach(id => {
|
||||
if (!(state.get(id) === undefined)) {
|
||||
map.setIn([id, 'hidden'], true);
|
||||
}
|
||||
});
|
||||
});
|
||||
case TIMELINE_DELETE:
|
||||
return deleteStatus(state, action.id, action.references);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
|
@ -26,6 +26,14 @@ import {
|
|||
PIN_SUCCESS,
|
||||
UNPIN_SUCCESS,
|
||||
} from '../actions/interactions';
|
||||
import {
|
||||
SCHEDULED_STATUSES_FETCH_REQUEST,
|
||||
SCHEDULED_STATUSES_FETCH_SUCCESS,
|
||||
SCHEDULED_STATUSES_FETCH_FAIL,
|
||||
SCHEDULED_STATUSES_EXPAND_REQUEST,
|
||||
SCHEDULED_STATUSES_EXPAND_SUCCESS,
|
||||
SCHEDULED_STATUSES_EXPAND_FAIL,
|
||||
} from '../actions/scheduled_statuses';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
favourites: ImmutableMap({
|
||||
|
@ -110,6 +118,16 @@ export default function statusLists(state = initialState, action) {
|
|||
return prependOneToList(state, 'pins', action.status);
|
||||
case UNPIN_SUCCESS:
|
||||
return removeOneFromList(state, 'pins', action.status);
|
||||
case SCHEDULED_STATUSES_FETCH_REQUEST:
|
||||
case SCHEDULED_STATUSES_EXPAND_REQUEST:
|
||||
return state.setIn(['scheduled_statuses', 'isLoading'], true);
|
||||
case SCHEDULED_STATUSES_FETCH_FAIL:
|
||||
case SCHEDULED_STATUSES_EXPAND_FAIL:
|
||||
return state.setIn(['scheduled_statuses', 'isLoading'], false);
|
||||
case SCHEDULED_STATUSES_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'scheduled_statuses', action.statuses, action.next);
|
||||
case SCHEDULED_STATUSES_EXPAND_SUCCESS:
|
||||
return appendToList(state, 'scheduled_statuses', action.statuses, action.next);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue