From 2496ec09a4edcea34f9c41fd3d53379228ba9611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Souza?= Date: Fri, 21 Oct 2016 20:42:14 -0200 Subject: [PATCH] Single file collections (#132) * Files based collections skeleton * listing file based cards * create new entry with collection * moved lookupEntry to main backend * Editing single page Collections file * List widget basic implementation * Adjustments for test-repo * check if value exists before trying to iterate over --- example/config.yml | 7 -- example/index.html | 2 +- src/backends/backend.js | 82 +++++++++++++++------- src/backends/github/API.js | 5 +- src/backends/github/implementation.js | 47 ++++++------- src/backends/test-repo/implementation.js | 17 +++-- src/components/ControlPanel/ControlPane.js | 26 +++---- src/components/EntryEditor/EntryEditor.js | 4 ++ src/components/EntryListing.js | 20 +++--- src/components/PreviewPane/Preview.js | 8 +-- src/components/PreviewPane/PreviewPane.js | 5 +- src/components/Widgets.js | 3 + src/components/Widgets/ListControl.js | 17 +++++ src/components/Widgets/ListPreview.js | 11 +++ src/constants/collectionTypes.js | 2 + src/containers/EntryPage.js | 21 +++++- src/reducers/collections.js | 14 +++- src/valueObjects/Entry.js | 1 + 18 files changed, 190 insertions(+), 102 deletions(-) create mode 100644 src/components/Widgets/ListControl.js create mode 100644 src/components/Widgets/ListPreview.js create mode 100644 src/constants/collectionTypes.js diff --git a/example/config.yml b/example/config.yml index a930e59b..3d5f55bf 100644 --- a/example/config.yml +++ b/example/config.yml @@ -37,13 +37,6 @@ collections: # A list of collections the CMS should be able to edit description: "General Site Settings" fields: - {label: "Global title", name: site_title, widget: "string"} - - label: "Post Settings" - name: posts - widget: "object" - fields: - - {label: "Number of posts on frontpage", name: front_limit, widget: number} - - {label: "Default Author", name: author, widget: string} - - {label: "Default Thumbnail", name: thumb, widget: image, class: "thumb"} - name: "authors" label: "Authors" diff --git a/example/index.html b/example/index.html index cbf4ef07..3edb2778 100644 --- a/example/index.html +++ b/example/index.html @@ -30,7 +30,7 @@ }, _data: { "settings.json": { - content: '{"site_title": "CMS Demo", "posts": {"front_limit": 5, "author": "Matt Biilmann"}}' + content: '{"site_title": "CMS Demo"}' }, "authors.yml": { content: 'authors:\n - name: Mathias\n description: Co-founder @ Netlify\n' diff --git a/src/backends/backend.js b/src/backends/backend.js index 6c6bb778..5d566cba 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -3,6 +3,7 @@ import GitHubBackend from './github/implementation'; import NetlifyGitBackend from './netlify-git/implementation'; import { resolveFormat } from '../formats/formats'; import { createEntry } from '../valueObjects/Entry'; +import { FILES, FOLDER } from '../constants/collectionTypes'; class LocalStorageAuthStore { storageKey = 'nf-cms-user'; @@ -18,15 +19,15 @@ class LocalStorageAuthStore { } const slugFormatter = (template, entryData) => { - var date = new Date(); - return template.replace(/\{\{([^\}]+)\}\}/g, function(_, name) { + const date = new Date(); + return template.replace(/\{\{([^\}]+)\}\}/g, (_, name) => { switch (name) { case 'year': return date.getFullYear(); case 'month': - return ('0' + (date.getMonth() + 1)).slice(-2); + return (`0${ date.getMonth() + 1 }`).slice(-2); case 'day': - return ('0' + date.getDate()).slice(-2); + return (`0${ date.getDate() }`).slice(-2); case 'slug': const identifier = entryData.get('title', entryData.get('path')); return identifier.trim().toLowerCase().replace(/[^a-z0-9\.\-\_]+/gi, '-'); @@ -65,13 +66,31 @@ class Backend { }); } - listEntries(collection, page, perPage) { - return this.implementation.entries(collection, page, perPage).then((response) => { - return { - pagination: response.pagination, - entries: response.entries.map(this.entryWithFormat(collection)) - }; - }); + listEntries(collection) { + const type = collection.get('type'); + if (type === FOLDER) { + return this.implementation.entriesByFolder(collection) + .then(loadedEntries => ( + loadedEntries.map(loadedEntry => createEntry(collection.get('name'), loadedEntry.file.path.split('/').pop().replace(/\.[^\.]+$/, ''), loadedEntry.file.path, { raw: loadedEntry.data })) + )) + .then(entries => ( + { + entries: entries.map(this.entryWithFormat(collection)), + } + )); + } else if (type === FILES) { + const collectionFiles = collection.get('files').map(collectionFile => ({ path: collectionFile.get('file'), label: collectionFile.get('label') })); + return this.implementation.entriesByFiles(collection, collectionFiles) + .then(loadedEntries => ( + loadedEntries.map(loadedEntry => createEntry(collection.get('name'), loadedEntry.file.path.split('/').pop().replace(/\.[^\.]+$/, ''), loadedEntry.file.path, { raw: loadedEntry.data, label: loadedEntry.file.label })) + )) + .then(entries => ( + { + entries: entries.map(this.entryWithFormat(collection)), + } + )); + } + return Promise.reject(`Couldn't process collection type ${ type }`); } // We have the file path. Fetch and parse the file. @@ -82,12 +101,19 @@ class Backend { // Will fetch the whole list of files from GitHub and load each file, then looks up for entry. // (Files are persisted in local storage - only expensive on the first run for each file). lookupEntry(collection, slug) { - return this.implementation.lookupEntry(collection, slug).then(this.entryWithFormat(collection)); + const type = collection.get('type'); + if (type === FOLDER) { + return this.implementation.entriesByFolder(collection) + .then(loadedEntries => ( + loadedEntries.map(loadedEntry => createEntry(collection.get('name'), loadedEntry.file.path.split('/').pop().replace(/\.[^\.]+$/, ''), loadedEntry.file.path, { raw: loadedEntry.data })) + )) + .then(response => response.filter(entry => entry.slug === slug)[0]) + .then(this.entryWithFormat(collection)); + } } newEntry(collection) { - const newEntry = createEntry(); - return this.entryWithFormat(collection)(newEntry); + return createEntry(collection.get('name')); } entryWithFormat(collectionOrEntity) { @@ -95,8 +121,10 @@ class Backend { const format = resolveFormat(collectionOrEntity, entry); if (entry && entry.raw) { entry.data = format && format.fromFile(entry.raw); + return entry; + } else { + return format.fromFile(entry); } - return entry; }; } @@ -104,7 +132,7 @@ class Backend { return this.implementation.unpublishedEntries(page, perPage).then((response) => { return { pagination: response.pagination, - entries: response.entries.map(this.entryWithFormat('editorialWorkflow')) + entries: response.entries.map(this.entryWithFormat('editorialWorkflow')), }; }); } @@ -126,28 +154,28 @@ class Backend { if (newEntry) { const slug = slugFormatter(collection.get('slug'), entryDraft.getIn(['entry', 'data'])); entryObj = { - path: `${collection.get('folder')}/${slug}.md`, - slug: slug, - raw: this.entryToRaw(collection, entryData) + path: `${ collection.get('folder') }/${ slug }.md`, + slug, + raw: this.entryToRaw(collection, entryData), }; } else { entryObj = { path: entryDraft.getIn(['entry', 'path']), slug: entryDraft.getIn(['entry', 'slug']), - raw: this.entryToRaw(collection, entryData) + raw: this.entryToRaw(collection, entryData), }; } - const commitMessage = (newEntry ? 'Created ' : 'Updated ') + - collection.get('label') + ' “' + - entryDraft.getIn(['entry', 'data', 'title']) + '”'; + const commitMessage = `${ (newEntry ? 'Created ' : 'Updated ') + + collection.get('label') } “${ + entryDraft.getIn(['entry', 'data', 'title']) }”`; const mode = config.get('publish_mode'); const collectionName = collection.get('name'); return this.implementation.persistEntry(entryObj, MediaFiles, { - newEntry, parsedData, commitMessage, collectionName, mode, ...options + newEntry, parsedData, commitMessage, collectionName, mode, ...options, }); } @@ -186,11 +214,11 @@ export function resolveBackend(config) { case 'netlify-git': return new Backend(new NetlifyGitBackend(config, slugFormatter), authStore); default: - throw `Backend not found: ${name}`; + throw `Backend not found: ${ name }`; } } -export const currentBackend = (function() { +export const currentBackend = (function () { let backend = null; return (config) => { @@ -199,4 +227,4 @@ export const currentBackend = (function() { return backend = resolveBackend(config); } }; -})(); +}()); diff --git a/src/backends/github/API.js b/src/backends/github/API.js index 645bd4d3..31efefbb 100644 --- a/src/backends/github/API.js +++ b/src/backends/github/API.js @@ -112,13 +112,14 @@ export default class API { const cache = LocalForage.getItem(`gh.meta.${ key }`); return cache.then((cached) => { if (cached && cached.expires > Date.now()) { return cached.data; } - + console.log("%c Checking for MetaData files", "line-height: 30px;text-align: center;font-weight: bold"); // eslint-disable-line return this.request(`${ this.repoURL }/contents/${ key }.json`, { params: { ref: 'refs/meta/_netlify_cms' }, headers: { Accept: 'application/vnd.github.VERSION.raw' }, cache: 'no-store', }) - .then(response => JSON.parse(response)); + .then(response => JSON.parse(response)) + .catch(error => console.log("%c %s does not have metadata", "line-height: 30px;text-align: center;font-weight: bold", key)); // eslint-disable-line }); } diff --git a/src/backends/github/implementation.js b/src/backends/github/implementation.js index 78a6d7d5..7283cf81 100644 --- a/src/backends/github/implementation.js +++ b/src/backends/github/implementation.js @@ -31,33 +31,30 @@ export default class GitHub { }); } - entries(collection) { - return this.api.listFiles(collection.get('folder')).then((files) => { - const sem = semaphore(MAX_CONCURRENT_DOWNLOADS); - const promises = []; - files.map((file) => { - promises.push(new Promise((resolve, reject) => { - return sem.take(() => this.api.readFile(file.path, file.sha).then((data) => { - resolve(createEntry(collection.get('name'), file.path.split('/').pop().replace(/\.[^\.]+$/, ''), file.path, { raw: data })); - sem.leave(); - }).catch((err) => { - sem.leave(); - reject(err); - })); - })); - }); - return Promise.all(promises); - }).then(entries => ({ - entries, - })); + entriesByFolder(collection) { + return this.api.listFiles(collection.get('folder')).then(files => this.entriesByFiles(collection, files)); } - - // Will fetch the entire list of entries from github. - lookupEntry(collection, slug) { - return this.entries(collection).then(response => ( - response.entries.filter(entry => entry.slug === slug)[0] - )); + entriesByFiles(collection, files) { + const sem = semaphore(MAX_CONCURRENT_DOWNLOADS); + const promises = []; + files.forEach((file) => { + promises.push(new Promise((resolve, reject) => { + return sem.take(() => this.api.readFile(file.path, file.sha).then((data) => { + resolve( + { + file, + data, + } + ); + sem.leave(); + }).catch((err) => { + sem.leave(); + reject(err); + })); + })); + }); + return Promise.all(promises); } // Fetches a single entry. diff --git a/src/backends/test-repo/implementation.js b/src/backends/test-repo/implementation.js index e0f9182e..d2e8ec84 100644 --- a/src/backends/test-repo/implementation.js +++ b/src/backends/test-repo/implementation.js @@ -24,18 +24,25 @@ export default class TestRepo { return Promise.resolve({ email: state.email }); } - entries(collection) { + entriesByFolder(collection) { const entries = []; const folder = collection.get('folder'); if (folder) { for (const path in window.repoFiles[folder]) { - entries.push(createEntry(collection.get('name'), getSlug(path), `${ folder }/${ path }`, { raw: window.repoFiles[folder][path].content })); + const file = { path: `${ folder }/${ path }` }; + entries.push( + { + file, + data: window.repoFiles[folder][path].content, + } + ); } } + return Promise.resolve(entries); + } - return Promise.resolve({ - entries, - }); + entriesByFiles(collection, files) { + throw new Error('Not implemented yet'); } lookupEntry(collection, slug) { diff --git a/src/components/ControlPanel/ControlPane.js b/src/components/ControlPanel/ControlPane.js index 10692cdf..4f8a96f3 100644 --- a/src/components/ControlPanel/ControlPane.js +++ b/src/components/ControlPanel/ControlPane.js @@ -6,7 +6,7 @@ import styles from './ControlPane.css'; export default class ControlPane extends Component { controlFor(field) { - const { entry, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props; + const { entry, fields, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props; const widget = resolveWidget(field.get('widget')); const fieldName = field.get('name'); const value = entry.getIn(['data', fieldName]); @@ -29,23 +29,22 @@ export default class ControlPane extends Component { } render() { - const { collection } = this.props; - if (!collection) { + const { collection, fields } = this.props; + if (!collection || !fields) { return null; } + return (
{ - collection - .get('fields') - .map(field => -
- {this.controlFor(field)} -
- ) + fields.map(field => +
+ {this.controlFor(field)} +
+ ) }
); @@ -55,6 +54,7 @@ export default class ControlPane extends Component { ControlPane.propTypes = { collection: ImmutablePropTypes.map.isRequired, entry: ImmutablePropTypes.map.isRequired, + fields: ImmutablePropTypes.list.isRequired, getMedia: PropTypes.func.isRequired, onAddMedia: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired, diff --git a/src/components/EntryEditor/EntryEditor.js b/src/components/EntryEditor/EntryEditor.js index dfab685d..92aae5c5 100644 --- a/src/components/EntryEditor/EntryEditor.js +++ b/src/components/EntryEditor/EntryEditor.js @@ -10,6 +10,7 @@ export default function EntryEditor( { collection, entry, + fields, getMedia, onChange, onAddMedia, @@ -26,6 +27,7 @@ export default function EntryEditor( @@ -56,6 +59,7 @@ export default function EntryEditor( EntryEditor.propTypes = { collection: ImmutablePropTypes.map.isRequired, entry: ImmutablePropTypes.map.isRequired, + fields: ImmutablePropTypes.list.isRequired, getMedia: PropTypes.func.isRequired, onAddMedia: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired, diff --git a/src/components/EntryListing.js b/src/components/EntryListing.js index 09fe5407..fd9a7375 100644 --- a/src/components/EntryListing.js +++ b/src/components/EntryListing.js @@ -21,7 +21,7 @@ export default class EntryListing extends React.Component { { mq: '1005px', columns: 4, gutter: 15 }, { mq: '1515px', columns: 5, gutter: 15 }, { mq: '1770px', columns: 6, gutter: 15 }, - ] + ], }; this.updateBricks = _.throttle(this.updateBricks.bind(this), 30); @@ -32,7 +32,7 @@ export default class EntryListing extends React.Component { this.bricksInstance = Bricks({ container: this._entries, packed: this.bricksConfig.packed, - sizes: this.bricksConfig.sizes + sizes: this.bricksConfig.sizes, }); this.bricksInstance.resize(true); @@ -65,10 +65,10 @@ export default class EntryListing extends React.Component { const card = Cards[cartType] || Cards._unknown; return React.createElement(card, { key: entry.get('slug'), - collection: collection, + collection, onClick: history.push.bind(this, link), onImageLoaded: this.updateBricks, - text: entry.getIn(['data', collection.getIn(['card', 'text'])]), + text: entry.get('label') ? entry.get('label') : entry.getIn(['data', collection.getIn(['card', 'text'])]), description: entry.getIn(['data', collection.getIn(['card', 'description'])]), image: entry.getIn(['data', collection.getIn(['card', 'image'])]), }); @@ -83,13 +83,13 @@ export default class EntryListing extends React.Component { if (Map.isMap(collections)) { const collectionName = collections.get('name'); return entries.map((entry) => { - const path = `/collections/${collectionName}/entries/${entry.get('slug')}`; + const path = `/collections/${ collectionName }/entries/${ entry.get('slug') }`; return this.cardFor(collections, entry, path); }); } else { return entries.map((entry) => { const collection = collections.filter(collection => collection.get('name') === entry.get('collection')).first(); - const path = `/collections/${collection.get('name')}/entries/${entry.get('slug')}`; + const path = `/collections/${ collection.get('name') }/entries/${ entry.get('slug') }`; return this.cardFor(collection, entry, path); }); } @@ -98,13 +98,13 @@ export default class EntryListing extends React.Component { render() { const { children } = this.props; const cards = this.renderCards(); - return
+ return (

{children}

-
this._entries = c}> +
this._entries = c}> {cards}
-
; +
); } } @@ -112,7 +112,7 @@ EntryListing.propTypes = { children: PropTypes.node.isRequired, collections: PropTypes.oneOfType([ ImmutablePropTypes.map, - ImmutablePropTypes.iterable + ImmutablePropTypes.iterable, ]).isRequired, entries: ImmutablePropTypes.list, onPaginate: PropTypes.func.isRequired, diff --git a/src/components/PreviewPane/Preview.js b/src/components/PreviewPane/Preview.js index 86f03071..ea7d4867 100644 --- a/src/components/PreviewPane/Preview.js +++ b/src/components/PreviewPane/Preview.js @@ -1,14 +1,13 @@ import React, { PropTypes } from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; -export default function Preview({ collection, widgetFor }) { - if (!collection) { +export default function Preview({ collection, fields, widgetFor }) { + if (!collection || !fields) { return null; } - return (
- {collection.get('fields').map(field => widgetFor(field.get('name')))} + {fields.map(field => widgetFor(field.get('name')))}
); } @@ -16,6 +15,7 @@ export default function Preview({ collection, widgetFor }) { Preview.propTypes = { collection: ImmutablePropTypes.map.isRequired, entry: ImmutablePropTypes.map.isRequired, + fields: ImmutablePropTypes.list.isRequired, getMedia: PropTypes.func.isRequired, widgetFor: PropTypes.func.isRequired, }; diff --git a/src/components/PreviewPane/PreviewPane.js b/src/components/PreviewPane/PreviewPane.js index 5ee0efd2..f3c99de2 100644 --- a/src/components/PreviewPane/PreviewPane.js +++ b/src/components/PreviewPane/PreviewPane.js @@ -14,8 +14,8 @@ export default class PreviewPane extends React.Component { } widgetFor = (name) => { - const { collection, entry, getMedia } = this.props; - const field = collection.get('fields').find(field => field.get('name') === name); + const { fields, entry, getMedia } = this.props; + const field = fields.find(field => field.get('name') === name); const widget = resolveWidget(field.get('widget')); return React.createElement(widget.preview, { key: field.get('name'), @@ -67,6 +67,7 @@ export default class PreviewPane extends React.Component { PreviewPane.propTypes = { collection: ImmutablePropTypes.map.isRequired, + fields: ImmutablePropTypes.list.isRequired, entry: ImmutablePropTypes.map.isRequired, getMedia: PropTypes.func.isRequired, scrollTop: PropTypes.number, diff --git a/src/components/Widgets.js b/src/components/Widgets.js index e731ed78..abb96b10 100644 --- a/src/components/Widgets.js +++ b/src/components/Widgets.js @@ -3,6 +3,8 @@ import UnknownControl from './Widgets/UnknownControl'; import UnknownPreview from './Widgets/UnknownPreview'; import StringControl from './Widgets/StringControl'; import StringPreview from './Widgets/StringPreview'; +import ListControl from './Widgets/ListControl'; +import ListPreview from './Widgets/ListPreview'; import TextControl from './Widgets/TextControl'; import TextPreview from './Widgets/TextPreview'; import MarkdownControl from './Widgets/MarkdownControl'; @@ -14,6 +16,7 @@ import DateTimePreview from './Widgets/DateTimePreview'; registry.registerWidget('string', StringControl, StringPreview); registry.registerWidget('text', TextControl, TextPreview); +registry.registerWidget('list', ListControl, ListPreview); registry.registerWidget('markdown', MarkdownControl, MarkdownPreview); registry.registerWidget('image', ImageControl, ImagePreview); registry.registerWidget('datetime', DateTimeControl, DateTimePreview); diff --git a/src/components/Widgets/ListControl.js b/src/components/Widgets/ListControl.js new file mode 100644 index 00000000..7d58de07 --- /dev/null +++ b/src/components/Widgets/ListControl.js @@ -0,0 +1,17 @@ +import React, { Component, PropTypes } from 'react'; + +export default class ListControl extends Component { + + static propTypes = { + onChange: PropTypes.func.isRequired, + value: PropTypes.node, + }; + handleChange = (e) => { + this.props.onChange(e.target.value.split(',').map(item => item.trim())); + }; + + render() { + const { value } = this.props; + return ; + } +} diff --git a/src/components/Widgets/ListPreview.js b/src/components/Widgets/ListPreview.js new file mode 100644 index 00000000..1b68a1ec --- /dev/null +++ b/src/components/Widgets/ListPreview.js @@ -0,0 +1,11 @@ +import React, { PropTypes } from 'react'; + +export default function ListPreview({ value }) { + return (
    + { value && value.map(item =>
  • {item}
  • ) } +
); +} + +ListPreview.propTypes = { + value: PropTypes.node, +}; diff --git a/src/constants/collectionTypes.js b/src/constants/collectionTypes.js new file mode 100644 index 00000000..c660c9bc --- /dev/null +++ b/src/constants/collectionTypes.js @@ -0,0 +1,2 @@ +export const FILES = 'file_based_collection'; +export const FOLDER = 'folder_based_collection'; diff --git a/src/containers/EntryPage.js b/src/containers/EntryPage.js index 9f366bd0..20c7289c 100644 --- a/src/containers/EntryPage.js +++ b/src/containers/EntryPage.js @@ -12,6 +12,7 @@ import { import { cancelEdit } from '../actions/editor'; import { addMedia, removeMedia } from '../actions/media'; import { selectEntry, getMedia } from '../reducers'; +import { FOLDER, FILES } from '../constants/collectionTypes'; import EntryEditor from '../components/EntryEditor/EntryEditor'; import entryPageHOC from './editorialWorkflow/EntryPageHOC'; import { Loader } from '../components/UI'; @@ -31,6 +32,7 @@ class EntryPage extends React.Component { persistEntry: PropTypes.func.isRequired, removeMedia: PropTypes.func.isRequired, cancelEdit: PropTypes.func.isRequired, + fields: ImmutablePropTypes.list.isRequired, slug: PropTypes.string, newEntry: PropTypes.bool.isRequired, }; @@ -41,7 +43,7 @@ class EntryPage extends React.Component { if (newEntry) { createEmptyDraft(collection); } else { - loadEntry(entry, collection, slug); + if (collection.get('type') === FOLDER) loadEntry(entry, collection, slug); this.createDraft(entry); } } @@ -72,6 +74,7 @@ class EntryPage extends React.Component { const { entry, entryDraft, + fields, boundGetMedia, collection, changeDraft, @@ -90,6 +93,7 @@ class EntryPage extends React.Component { entry={entryDraft.get('entry')} getMedia={boundGetMedia} collection={collection} + fields={fields} onChange={changeDraft} onAddMedia={addMedia} onRemoveMedia={removeMedia} @@ -102,9 +106,19 @@ class EntryPage extends React.Component { function mapStateToProps(state, ownProps) { const { collections, entryDraft } = state; - const collection = collections.get(ownProps.params.name); - const newEntry = ownProps.route && ownProps.route.newRecord === true; const slug = ownProps.params.slug; + const collection = collections.get(ownProps.params.name); + + let fields; + if (collection.get('type') === FOLDER) { + fields = collection.get('fields'); + } else { + const files = collection.get('files'); + const file = files.filter(f => f.get('name') === slug); + fields = file.getIn([0, 'fields']); + } + const newEntry = ownProps.route && ownProps.route.newRecord === true; + const entry = newEntry ? null : selectEntry(state, collection.get('name'), slug); const boundGetMedia = getMedia.bind(null, state); return { @@ -113,6 +127,7 @@ function mapStateToProps(state, ownProps) { newEntry, entryDraft, boundGetMedia, + fields, slug, entry, }; diff --git a/src/reducers/collections.js b/src/reducers/collections.js index 5ede1aff..8d0bf0f6 100644 --- a/src/reducers/collections.js +++ b/src/reducers/collections.js @@ -1,13 +1,21 @@ import { OrderedMap, fromJS } from 'immutable'; import { CONFIG_SUCCESS } from '../actions/config'; +import { FILES, FOLDER } from '../constants/collectionTypes'; + +const hasProperty = (config, property) => ({}.hasOwnProperty.call(config, property)); const collections = (state = null, action) => { switch (action.type) { case CONFIG_SUCCESS: - const collections = action.payload && action.payload.collections; + const configCollections = action.payload && action.payload.collections; return OrderedMap().withMutations((map) => { - (collections || []).forEach(function(collection) { - map.set(collection.name, fromJS(collection)); + (configCollections || []).forEach((configCollection) => { + if (hasProperty(configCollection, 'folder')) { + configCollection.type = FOLDER; // eslint-disable-line no-param-reassign + } else if (hasProperty(configCollection, 'files')) { + configCollection.type = FILES; // eslint-disable-line no-param-reassign + } + map.set(configCollection.name, fromJS(configCollection)); }); }); default: diff --git a/src/valueObjects/Entry.js b/src/valueObjects/Entry.js index ba000b24..0798960c 100644 --- a/src/valueObjects/Entry.js +++ b/src/valueObjects/Entry.js @@ -6,6 +6,7 @@ export function createEntry(collection, slug = '', path = '', options = {}) { returnObj.partial = options.partial || false; returnObj.raw = options.raw || ''; returnObj.data = options.data || {}; + returnObj.label = options.label || null; returnObj.metaData = options.metaData || null; return returnObj; }