Merge pull request #293 from Benaiah/label-cards-in-editorial-workflow

Add meta info to cards in editorial workflow
This commit is contained in:
Shawn Erquhart 2017-03-20 20:30:09 -04:00 committed by GitHub
commit 96d6242b78
9 changed files with 116 additions and 13 deletions

View File

@ -124,7 +124,15 @@ class Backend {
.then(loadedEntries => loadedEntries.filter(entry => entry !== null))
.then(entries => (
entries.map((loadedEntry) => {
const entry = createEntry(loadedEntry.metaData.collection, loadedEntry.slug, loadedEntry.file.path, { raw: loadedEntry.data });
const entry = createEntry(
loadedEntry.metaData.collection,
loadedEntry.slug,
loadedEntry.file.path,
{
raw: loadedEntry.data,
isModification: loadedEntry.isModification,
}
);
entry.metaData = loadedEntry.metaData;
return entry;
})
@ -138,7 +146,14 @@ class Backend {
unpublishedEntry(collection, slug) {
return this.implementation.unpublishedEntry(collection, slug)
.then((loadedEntry) => {
const entry = createEntry("draft", loadedEntry.slug, loadedEntry.file.path, { raw: loadedEntry.data });
const entry = createEntry(
"draft",
loadedEntry.slug,
loadedEntry.file.path,
{
raw: loadedEntry.data,
isModification: loadedEntry.isModification,
});
entry.metaData = loadedEntry.metaData;
return entry;
})

View File

@ -1,7 +1,7 @@
import LocalForage from "localforage";
import { Base64 } from "js-base64";
import _ from "lodash";
import { filterPromises } from "../../lib/promiseHelper";
import { filterPromises, resolvePromiseProperties } from "../../lib/promiseHelper";
import AssetProxy from "../../valueObjects/AssetProxy";
import { SIMPLE, EDITORIAL_WORKFLOW, status } from "../../constants/publishModes";
import { APIError, EditorialWorkflowError } from "../../valueObjects/errors";
@ -159,20 +159,29 @@ export default class API {
}
readUnpublishedBranchFile(contentKey) {
let metaData;
const unpublishedPromise = this.retrieveMetadata(contentKey)
.then((data) => {
metaData = data;
if (data.objects.entry.path) {
return this.readFile(data.objects.entry.path, null, data.branch);
}
return Promise.reject(null);
const metaDataPromise = this.retrieveMetadata(contentKey)
.then(data => (data.objects.entry.path ? data : Promise.reject(null)));
return resolvePromiseProperties({
metaData: metaDataPromise,
fileData: metaDataPromise.then(
data => this.readFile(data.objects.entry.path, null, data.branch)),
isModification: metaDataPromise.then(
data => this.isUnpublishedEntryModification(data.objects.entry.path, this.branch)),
})
.then(fileData => ({ metaData, fileData }))
.catch(() => {
throw new EditorialWorkflowError('content is not under editorial workflow', true);
});
return unpublishedPromise;
}
isUnpublishedEntryModification(path, branch) {
return this.readFile(path, null, branch)
.then(data => true)
.catch((err) => {
if (err.message && err.message === "Not Found") {
return false;
}
throw err;
});
}
listUnpublishedBranches() {

View File

@ -99,6 +99,7 @@ export default class GitHub {
file: { path },
data: data.fileData,
metaData: data.metaData,
isModification: data.isModification,
});
sem.leave();
}
@ -127,6 +128,7 @@ export default class GitHub {
file: { path: data.metaData.objects.entry.path },
data: data.fileData,
metaData: data.metaData,
isModification: data.isModification,
};
});
}

View File

@ -2,12 +2,14 @@
--defaultColor: #333;
--defaultColorLight: #eee;
--backgroundColor: #fff;
--backgroundColorShaded: #eee;
--shadowColor: rgba(0, 0, 0, .25);
--infoColor: #69c;
--successColor: #1c7;
--warningColor: #fa0;
--errorColor: #f52;
--borderRadius: 2px;
--borderRadiusLarge: 10px;
--topmostZindex: 99999;
--foregroundAltColor: #fff;
--backgroundAltColor: #272e30;

View File

@ -3,8 +3,11 @@ import { DragSource, DropTarget, HTML5DragDrop } from 'react-simple-dnd';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Link } from 'react-router';
import moment from 'moment';
import pluralize from 'pluralize';
import { capitalize } from 'lodash'
import { Card, CardTitle, CardText, CardActions } from 'react-toolbox/lib/card';
import Button from 'react-toolbox/lib/button';
import UnpublishedListingCardMeta from './UnpublishedListingCardMeta.js';
import { status, statusDescriptions } from '../../constants/publishModes';
import styles from './UnpublishedListing.css';
@ -68,6 +71,7 @@ class UnpublishedListing extends React.Component {
const slug = entry.get('slug');
const ownStatus = entry.getIn(['metaData', 'status']);
const collection = entry.getIn(['metaData', 'collection']);
const isModification = entry.get('isModification');
return (
<DragSource
key={slug}
@ -77,6 +81,10 @@ class UnpublishedListing extends React.Component {
>
<div className={styles.draggable}>
<Card className={styles.card}>
<UnpublishedListingCardMeta
meta={capitalize(pluralize(collection))}
label={isModification ? "" : "New"}
/>
<CardTitle
title={entry.getIn(['data', 'title'])}
subtitle={`by ${ author }`}

View File

@ -0,0 +1,25 @@
@import '../UI/theme.css';
.cardMeta {
display: flex;
align-items: center;
justify-content: space-between;
height: 34px;
padding: 0 16px;
margin-bottom: -6px;
font-size: .75em;
text-transform: uppercase;
background: var(--backgroundColorShaded);
}
.meta {}
.label {
padding: 5px 8px 4px 8px;
border-radius: var(--borderRadiusLarge);
background: var(--backgroundAltColor);
color: var(--defaultColorLight)
}

View File

@ -0,0 +1,17 @@
import React, { PropTypes } from 'react';
import styles from './UnpublishedListingCardMeta.css';
const UnpublishedListingCardMeta = ({ meta, label }) =>
<div className={styles.cardMeta}>
<span className={styles.meta}>{meta}</span>
{(label && label.length > 0)
? <span className={styles.label}>{label}</span>
: ""}
</div>;
UnpublishedListingCardMeta.propTypes = {
meta: PropTypes.string.isRequired,
label: PropTypes.string,
};
export default UnpublishedListingCardMeta;

View File

@ -1,3 +1,23 @@
import { zipObject } from 'lodash';
export const filterPromises = (arr, filter) =>
Promise.all(arr.map(entry => filter(entry)))
.then(bits => arr.filter(entry => bits.shift()));
export const resolvePromiseProperties = obj =>
(new Promise((resolve, reject) => {
// Get the keys which represent promises
const promiseKeys = Object.keys(obj).filter(
key => obj[key] instanceof Promise);
const promises = promiseKeys.map(key => obj[key]);
// Resolve all promises
Promise.all(promises)
.then(resolvedPromises => resolve(
// Return a copy of obj with promises overwritten by their
// resolved values
Object.assign(obj, zipObject(promiseKeys, resolvedPromises))))
// Pass errors to outer promise chain
.catch(err => reject(err));
}));

View File

@ -1,3 +1,5 @@
import { isBoolean } from "lodash";
export function createEntry(collection, slug = '', path = '', options = {}) {
const returnObj = {};
returnObj.collection = collection;
@ -8,5 +10,8 @@ export function createEntry(collection, slug = '', path = '', options = {}) {
returnObj.data = options.data || {};
returnObj.label = options.label || null;
returnObj.metaData = options.metaData || null;
returnObj.isModification = isBoolean(options.isModification)
? options.isModification
: null;
return returnObj;
}