ScheduledStatuses: scaffolding

This commit is contained in:
Alex Gleason 2021-06-27 13:59:10 -05:00
parent f64f39eb19
commit 5f9d95301c
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
6 changed files with 230 additions and 0 deletions

View 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,
};
};

View 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>
);
}
}

View file

@ -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} />

View file

@ -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');
}

View 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;
}
};

View file

@ -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;
}