diff --git a/src/actions/editorialWorkflow.js b/src/actions/editorialWorkflow.js index 0c6d6817..5c906f1c 100644 --- a/src/actions/editorialWorkflow.js +++ b/src/actions/editorialWorkflow.js @@ -3,6 +3,7 @@ import { EDITORIAL_WORKFLOW } from '../constants/publishModes'; /* * Contant Declarations */ +export const INIT = 'init'; export const UNPUBLISHED_ENTRIES_REQUEST = 'UNPUBLISHED_ENTRIES_REQUEST'; export const UNPUBLISHED_ENTRIES_SUCCESS = 'UNPUBLISHED_ENTRIES_SUCCESS'; export const UNPUBLISHED_ENTRIES_FAILURE = 'UNPUBLISHED_ENTRIES_FAILURE'; @@ -35,6 +36,16 @@ function unpublishedEntriesFailed(error) { }; } +/* + * Exported simple Action Creators + */ +export function init() { + return { + type: INIT + }; +} + + /* * Exported Thunk Action Creators */ diff --git a/src/backends/github/API.js b/src/backends/github/API.js index c4cbc03c..9fe61b71 100644 --- a/src/backends/github/API.js +++ b/src/backends/github/API.js @@ -1,11 +1,13 @@ import LocalForage from 'localforage'; import MediaProxy from '../../valueObjects/MediaProxy'; import { Base64 } from 'js-base64'; -import { EDITORIAL_WORKFLOW } from '../../constants/publishModes'; +import { EDITORIAL_WORKFLOW, status } from '../../constants/publishModes'; const API_ROOT = 'https://api.github.com'; export default class API { + + constructor(token, repo, branch) { this.token = token; this.repo = repo; @@ -192,7 +194,7 @@ export default class API { return this.createBranch(branchName, response.sha) .then(this.storeMetadata(contentKey, { type: 'PR', - status: 'draft', + status: status.DRAFT, branch: branchName, collection: options.collectionName, title: options.parsedData.title, diff --git a/src/constants/publishModes.js b/src/constants/publishModes.js index 308f5696..9e7256d9 100644 --- a/src/constants/publishModes.js +++ b/src/constants/publishModes.js @@ -1,3 +1,18 @@ -// Create/edit workflows +import { Map } from 'immutable'; + +// Create/edit workflow modes export const SIMPLE = 'simple'; export const EDITORIAL_WORKFLOW = 'editorial_workflow'; + +// Available status +export const status = { + DRAFT: 'draft', + PENDING_REVIEW: 'pending_review', + PENDING_PUBLISH: 'pending_publish', +}; + +export const statusDescriptions = Map({ + [status.DRAFT]: 'Draft', + [status.PENDING_REVIEW]: 'Waiting for Review', + [status.PENDING_PUBLISH]: 'Waiting to go live', +}); diff --git a/src/containers/CollectionPage.js b/src/containers/CollectionPage.js index cad69722..5790fbf0 100644 --- a/src/containers/CollectionPage.js +++ b/src/containers/CollectionPage.js @@ -2,15 +2,14 @@ import React, { PropTypes } from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; import { loadEntries } from '../actions/entries'; -import { loadUnpublishedEntries } from '../actions/editorialWorkflow'; import { selectEntries } from '../reducers'; import { Loader } from '../components/UI'; import EntryListing from '../components/EntryListing'; +import EditorialWorkflow from './EditorialWorkflowHoC'; class DashboardPage extends React.Component { componentDidMount() { const { collection, dispatch } = this.props; - dispatch(loadUnpublishedEntries()); if (collection) { dispatch(loadEntries(collection)); } @@ -38,7 +37,6 @@ class DashboardPage extends React.Component { ; } } - DashboardPage.propTypes = { collection: ImmutablePropTypes.map.isRequired, collections: ImmutablePropTypes.orderedMap.isRequired, @@ -46,6 +44,13 @@ DashboardPage.propTypes = { entries: ImmutablePropTypes.list, }; +/* + * Instead of checking the publish mode everywhere to dispatch & render the additional editorial workflow stuff, + * We delegate it to a Higher Order Component + */ +DashboardPage = EditorialWorkflow(DashboardPage); + + function mapStateToProps(state, ownProps) { const { collections } = state; const { name, slug } = ownProps.params; diff --git a/src/containers/EditorialWorkflowHoC.js b/src/containers/EditorialWorkflowHoC.js new file mode 100644 index 00000000..3c626d85 --- /dev/null +++ b/src/containers/EditorialWorkflowHoC.js @@ -0,0 +1,52 @@ +import React, { PropTypes } from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { init, loadUnpublishedEntries } from '../actions/editorialWorkflow'; +import { selectUnpublishedEntries } from '../reducers'; +import { EDITORIAL_WORKFLOW } from '../constants/publishModes'; +import { connect } from 'react-redux'; + +export default function EditorialWorkflow(WrappedComponent) { + class EditorialWorkflow extends WrappedComponent { + + componentDidMount() { + const { dispatch, isEditorialWorkflow } = this.props; + if (isEditorialWorkflow) { + dispatch(init()); + dispatch(loadUnpublishedEntries()); + } + super.componentDidMount(); + } + + render() { + const { isEditorialWorkflow } = this.props; + if (!isEditorialWorkflow) return super.render(); + + return ( +
+

HOC

+ {super.render()} +
+ ); + } + } + + EditorialWorkflow.propTypes = { + dispatch: PropTypes.func.isRequired, + isEditorialWorkflow: PropTypes.bool.isRequired, + unpublishedEntries: ImmutablePropTypes.list, + }; + + function mapStateToProps(state) { + const publish_mode = state.config.get('publish_mode'); + const isEditorialWorkflow = (publish_mode === EDITORIAL_WORKFLOW); + const returnObj = { isEditorialWorkflow }; + + if (isEditorialWorkflow) { + returnObj.unpublishedEntries = selectUnpublishedEntries(state, 'draft'); + } + + return returnObj; + } + + return connect(mapStateToProps)(EditorialWorkflow); +} diff --git a/src/reducers/editorialWorkflow.js b/src/reducers/editorialWorkflow.js index aef48f0d..10307bfd 100644 --- a/src/reducers/editorialWorkflow.js +++ b/src/reducers/editorialWorkflow.js @@ -1,10 +1,13 @@ import { Map, List, fromJS } from 'immutable'; import { - UNPUBLISHED_ENTRIES_REQUEST, UNPUBLISHED_ENTRIES_SUCCESS + INIT, UNPUBLISHED_ENTRIES_REQUEST, UNPUBLISHED_ENTRIES_SUCCESS } from '../actions/editorialWorkflow'; -const unpublishedEntries = (state = Map({ entities: Map(), pages: Map() }), action) => { +const unpublishedEntries = (state = null, action) => { switch (action.type) { + case INIT: + // Editorial workflow must be explicitly initiated. + return Map({ entities: Map(), pages: Map() }); case UNPUBLISHED_ENTRIES_REQUEST: return state.setIn(['pages', 'isFetching'], true); @@ -29,9 +32,9 @@ export const selectUnpublishedEntry = (state, status, slug) => ( ); export const selectUnpublishedEntries = (state, status) => { + if (!state) return; const slugs = state.getIn(['pages', 'ids']); return slugs && slugs.map((slug) => selectUnpublishedEntry(state, status, slug)); }; - export default unpublishedEntries;