editorial workflow HoC

This commit is contained in:
Cássio Zen 2016-09-08 16:18:38 -03:00
parent 90d4b39fc1
commit 04c50d8def
6 changed files with 97 additions and 9 deletions

View File

@ -3,6 +3,7 @@ import { EDITORIAL_WORKFLOW } from '../constants/publishModes';
/* /*
* Contant Declarations * Contant Declarations
*/ */
export const INIT = 'init';
export const UNPUBLISHED_ENTRIES_REQUEST = 'UNPUBLISHED_ENTRIES_REQUEST'; export const UNPUBLISHED_ENTRIES_REQUEST = 'UNPUBLISHED_ENTRIES_REQUEST';
export const UNPUBLISHED_ENTRIES_SUCCESS = 'UNPUBLISHED_ENTRIES_SUCCESS'; export const UNPUBLISHED_ENTRIES_SUCCESS = 'UNPUBLISHED_ENTRIES_SUCCESS';
export const UNPUBLISHED_ENTRIES_FAILURE = 'UNPUBLISHED_ENTRIES_FAILURE'; 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 * Exported Thunk Action Creators
*/ */

View File

@ -1,11 +1,13 @@
import LocalForage from 'localforage'; import LocalForage from 'localforage';
import MediaProxy from '../../valueObjects/MediaProxy'; import MediaProxy from '../../valueObjects/MediaProxy';
import { Base64 } from 'js-base64'; 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'; const API_ROOT = 'https://api.github.com';
export default class API { export default class API {
constructor(token, repo, branch) { constructor(token, repo, branch) {
this.token = token; this.token = token;
this.repo = repo; this.repo = repo;
@ -192,7 +194,7 @@ export default class API {
return this.createBranch(branchName, response.sha) return this.createBranch(branchName, response.sha)
.then(this.storeMetadata(contentKey, { .then(this.storeMetadata(contentKey, {
type: 'PR', type: 'PR',
status: 'draft', status: status.DRAFT,
branch: branchName, branch: branchName,
collection: options.collectionName, collection: options.collectionName,
title: options.parsedData.title, title: options.parsedData.title,

View File

@ -1,3 +1,18 @@
// Create/edit workflows import { Map } from 'immutable';
// Create/edit workflow modes
export const SIMPLE = 'simple'; export const SIMPLE = 'simple';
export const EDITORIAL_WORKFLOW = 'editorial_workflow'; 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',
});

View File

@ -2,15 +2,14 @@ import React, { PropTypes } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { loadEntries } from '../actions/entries'; import { loadEntries } from '../actions/entries';
import { loadUnpublishedEntries } from '../actions/editorialWorkflow';
import { selectEntries } from '../reducers'; import { selectEntries } from '../reducers';
import { Loader } from '../components/UI'; import { Loader } from '../components/UI';
import EntryListing from '../components/EntryListing'; import EntryListing from '../components/EntryListing';
import EditorialWorkflow from './EditorialWorkflowHoC';
class DashboardPage extends React.Component { class DashboardPage extends React.Component {
componentDidMount() { componentDidMount() {
const { collection, dispatch } = this.props; const { collection, dispatch } = this.props;
dispatch(loadUnpublishedEntries());
if (collection) { if (collection) {
dispatch(loadEntries(collection)); dispatch(loadEntries(collection));
} }
@ -38,7 +37,6 @@ class DashboardPage extends React.Component {
</div>; </div>;
} }
} }
DashboardPage.propTypes = { DashboardPage.propTypes = {
collection: ImmutablePropTypes.map.isRequired, collection: ImmutablePropTypes.map.isRequired,
collections: ImmutablePropTypes.orderedMap.isRequired, collections: ImmutablePropTypes.orderedMap.isRequired,
@ -46,6 +44,13 @@ DashboardPage.propTypes = {
entries: ImmutablePropTypes.list, 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) { function mapStateToProps(state, ownProps) {
const { collections } = state; const { collections } = state;
const { name, slug } = ownProps.params; const { name, slug } = ownProps.params;

View File

@ -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 (
<div>
<h2>HOC</h2>
{super.render()}
</div>
);
}
}
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);
}

View File

@ -1,10 +1,13 @@
import { Map, List, fromJS } from 'immutable'; import { Map, List, fromJS } from 'immutable';
import { import {
UNPUBLISHED_ENTRIES_REQUEST, UNPUBLISHED_ENTRIES_SUCCESS INIT, UNPUBLISHED_ENTRIES_REQUEST, UNPUBLISHED_ENTRIES_SUCCESS
} from '../actions/editorialWorkflow'; } from '../actions/editorialWorkflow';
const unpublishedEntries = (state = Map({ entities: Map(), pages: Map() }), action) => { const unpublishedEntries = (state = null, action) => {
switch (action.type) { switch (action.type) {
case INIT:
// Editorial workflow must be explicitly initiated.
return Map({ entities: Map(), pages: Map() });
case UNPUBLISHED_ENTRIES_REQUEST: case UNPUBLISHED_ENTRIES_REQUEST:
return state.setIn(['pages', 'isFetching'], true); return state.setIn(['pages', 'isFetching'], true);
@ -29,9 +32,9 @@ export const selectUnpublishedEntry = (state, status, slug) => (
); );
export const selectUnpublishedEntries = (state, status) => { export const selectUnpublishedEntries = (state, status) => {
if (!state) return;
const slugs = state.getIn(['pages', 'ids']); const slugs = state.getIn(['pages', 'ids']);
return slugs && slugs.map((slug) => selectUnpublishedEntry(state, status, slug)); return slugs && slugs.map((slug) => selectUnpublishedEntry(state, status, slug));
}; };
export default unpublishedEntries; export default unpublishedEntries;