From b717874e7b8fdeff7a1c756a378af76425a3fff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Zen?= Date: Wed, 24 Aug 2016 21:36:44 -0300 Subject: [PATCH] Allow the creation of new entries --- src/actions/entries.js | 17 +++++++++++ src/backends/backend.js | 6 ++++ src/backends/github/implementation.js | 5 ++- src/backends/test-repo/implementation.js | 7 ++--- src/components/Widgets/StringControl.js | 2 +- src/containers/EntryPage.js | 39 ++++++++++++++++-------- src/reducers/entryDraft.js | 15 ++++----- src/routing/routes.js | 3 +- src/valueObjects/Entry.js | 8 +++++ 9 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 src/valueObjects/Entry.js diff --git a/src/actions/entries.js b/src/actions/entries.js index 0f49031a..2f060a32 100644 --- a/src/actions/entries.js +++ b/src/actions/entries.js @@ -13,6 +13,7 @@ export const ENTRIES_SUCCESS = 'ENTRIES_SUCCESS'; export const ENTRIES_FAILURE = 'ENTRIES_FAILURE'; export const DRAFT_CREATE_FROM_ENTRY = 'DRAFT_CREATE_FROM_ENTRY'; +export const DRAFT_CREATE_EMPTY = 'DRAFT_CREATE_EMPTY'; export const DRAFT_DISCARD = 'DRAFT_DISCARD'; export const DRAFT_CHANGE = 'DRAFT_CHANGE'; @@ -102,6 +103,13 @@ function entryPersistFail(collection, entry, error) { }; } +function emmptyDraftCreated(entry) { + return { + type: DRAFT_CREATE_EMPTY, + payload: entry + }; +} + /* * Exported simple Action Creators */ @@ -153,6 +161,15 @@ export function loadEntries(collection) { }; } +export function createEmptyDraft(collection) { + return (dispatch, getState) => { + const state = getState(); + const backend = currentBackend(state.config); + const newEntry = backend.newEntry(collection); + dispatch(emmptyDraftCreated(newEntry)); + }; +} + export function persistEntry(collection, entry) { return (dispatch, getState) => { const state = getState(); diff --git a/src/backends/backend.js b/src/backends/backend.js index e66ab9cd..049f1262 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -1,6 +1,7 @@ import TestRepoBackend from './test-repo/implementation'; import GitHubBackend from './github/implementation'; import { resolveFormat } from '../formats/formats'; +import { createEntry } from '../valueObjects/Entry'; class LocalStorageAuthStore { storageKey = 'nf-cms-user'; @@ -57,6 +58,11 @@ class Backend { return this.implementation.entry(collection, slug).then(this.entryWithFormat(collection)); } + newEntry(collection) { + const newEntry = createEntry(); + return this.entryWithFormat(collection)(newEntry); + } + entryWithFormat(collection) { return (entry) => { const format = resolveFormat(collection, entry); diff --git a/src/backends/github/implementation.js b/src/backends/github/implementation.js index 67ad9535..194dd98b 100644 --- a/src/backends/github/implementation.js +++ b/src/backends/github/implementation.js @@ -1,5 +1,6 @@ import LocalForage from 'localforage'; import MediaProxy from '../../valueObjects/MediaProxy'; +import { createEntry } from '../../valueObjects/Entry'; import AuthenticationPage from './AuthenticationPage'; import { Base64 } from 'js-base64'; @@ -210,9 +211,7 @@ export default class GitHub { return this.api.listFiles(collection.get('folder')).then((files) => ( Promise.all(files.map((file) => ( this.api.readFile(file.path, file.sha).then((data) => { - file.slug = file.path.split('/').pop().replace(/\.[^\.]+$/, ''); - file.raw = data; - return file; + return createEntry(file.path, file.path.split('/').pop().replace(/\.[^\.]+$/, ''), data); }) ))) )).then((entries) => ({ diff --git a/src/backends/test-repo/implementation.js b/src/backends/test-repo/implementation.js index c4f41080..806791e2 100644 --- a/src/backends/test-repo/implementation.js +++ b/src/backends/test-repo/implementation.js @@ -1,4 +1,5 @@ import AuthenticationPage from './AuthenticationPage'; +import { createEntry } from '../../valueObjects/Entry'; function getSlug(path) { const m = path.match(/([^\/]+?)(\.[^\/\.]+)?$/); @@ -28,11 +29,7 @@ export default class TestRepo { const folder = collection.get('folder'); if (folder) { for (var path in window.repoFiles[folder]) { - entries.push({ - path: folder + '/' + path, - slug: getSlug(path), - raw: window.repoFiles[folder][path].content - }); + entries.push(createEntry(folder + '/' + path, getSlug(path), window.repoFiles[folder][path].content)); } } diff --git a/src/components/Widgets/StringControl.js b/src/components/Widgets/StringControl.js index b159a6e9..43de2170 100644 --- a/src/components/Widgets/StringControl.js +++ b/src/components/Widgets/StringControl.js @@ -11,7 +11,7 @@ export default class StringControl extends React.Component { } render() { - return ; + return ; } } diff --git a/src/containers/EntryPage.js b/src/containers/EntryPage.js index 79784c0e..81c9947b 100644 --- a/src/containers/EntryPage.js +++ b/src/containers/EntryPage.js @@ -4,6 +4,7 @@ import { connect } from 'react-redux'; import { loadEntry, createDraftFromEntry, + createEmptyDraft, discardDraft, changeDraft, persistEntry @@ -15,19 +16,27 @@ import EntryEditor from '../components/EntryEditor'; class EntryPage extends React.Component { constructor(props) { super(props); - this.props.loadEntry(props.collection, props.slug); + this.createDraft = this.createDraft.bind(this); this.handlePersistEntry = this.handlePersistEntry.bind(this); } componentDidMount() { - if (this.props.entry) { - this.props.createDraftFromEntry(this.props.entry); + if (!this.props.newEntry) { + this.props.loadEntry(this.props.collection, this.props.slug); + + this.createDraft(this.props.entry); + } else { + this.props.createEmptyDraft(this.props.collection); } } componentWillReceiveProps(nextProps) { - if (this.props.entry !== nextProps.entry && !nextProps.entry.get('isFetching')) { - this.props.createDraftFromEntry(nextProps.entry); + if (this.props.entry === nextProps.entry) return; + + if (nextProps.entry && !nextProps.entry.get('isFetching')) { + this.createDraft(nextProps.entry); + } else if (nextProps.newEntry) { + this.props.createEmptyDraft(nextProps.collection); } } @@ -35,17 +44,19 @@ class EntryPage extends React.Component { this.props.discardDraft(); } + createDraft(entry) { + if (entry) this.props.createDraftFromEntry(entry); + } + handlePersistEntry() { this.props.persistEntry(this.props.collection, this.props.entryDraft); } render() { - const { entry, entryDraft, boundGetMedia, collection, changeDraft, addMedia, removeMedia } = this.props; - - if (entry == null || entryDraft.get('entry') == undefined || entry.get('isFetching')) { + if (entryDraft == null || entryDraft.get('entry') == undefined || entry && entry.get('isFetching')) { return
Loading...
; } return ( @@ -68,22 +79,25 @@ EntryPage.propTypes = { changeDraft: PropTypes.func.isRequired, collection: ImmutablePropTypes.map.isRequired, createDraftFromEntry: PropTypes.func.isRequired, + createEmptyDraft: PropTypes.func.isRequired, discardDraft: PropTypes.func.isRequired, - entry: ImmutablePropTypes.map.isRequired, + entry: ImmutablePropTypes.map, entryDraft: ImmutablePropTypes.map.isRequired, loadEntry: PropTypes.func.isRequired, persistEntry: PropTypes.func.isRequired, removeMedia: PropTypes.func.isRequired, - slug: PropTypes.string.isRequired, + slug: PropTypes.string, + newEntry: PropTypes.bool.isRequired, }; 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 entry = selectEntry(state, collection.get('name'), slug); + const entry = newEntry ? null : selectEntry(state, collection.get('name'), slug); const boundGetMedia = getMedia.bind(null, state); - return { collection, collections, entryDraft, boundGetMedia, slug, entry }; + return { collection, collections, newEntry, entryDraft, boundGetMedia, slug, entry }; } export default connect( @@ -94,6 +108,7 @@ export default connect( removeMedia, loadEntry, createDraftFromEntry, + createEmptyDraft, discardDraft, persistEntry } diff --git a/src/reducers/entryDraft.js b/src/reducers/entryDraft.js index 8f23e8c9..c058742d 100644 --- a/src/reducers/entryDraft.js +++ b/src/reducers/entryDraft.js @@ -1,5 +1,5 @@ -import { Map, List } from 'immutable'; -import { DRAFT_CREATE_FROM_ENTRY, DRAFT_DISCARD, DRAFT_CHANGE } from '../actions/entries'; +import { Map, List, fromJS } from 'immutable'; +import { DRAFT_CREATE_FROM_ENTRY, DRAFT_CREATE_EMPTY, DRAFT_DISCARD, DRAFT_CHANGE } from '../actions/entries'; import { ADD_MEDIA, REMOVE_MEDIA } from '../actions/media'; const initialState = Map({ entry: Map(), mediaFiles: List() }); @@ -7,14 +7,15 @@ const initialState = Map({ entry: Map(), mediaFiles: List() }); const entryDraft = (state = Map(), action) => { switch (action.type) { case DRAFT_CREATE_FROM_ENTRY: - if (!action.payload) { - // New entry - return initialState; - } // Existing Entry return state.withMutations((state) => { state.set('entry', action.payload); - state.setIn(['entry', 'newRecord'], false); + state.set('mediaFiles', List()); + }); + case DRAFT_CREATE_EMPTY: + // New Entry + return state.withMutations((state) => { + state.set('entry', fromJS(action.payload)); state.set('mediaFiles', List()); }); case DRAFT_DISCARD: diff --git a/src/routing/routes.js b/src/routing/routes.js index 39f1bf81..59f2aa7f 100644 --- a/src/routing/routes.js +++ b/src/routing/routes.js @@ -10,7 +10,8 @@ export default ( - + + diff --git a/src/valueObjects/Entry.js b/src/valueObjects/Entry.js new file mode 100644 index 00000000..36ce0de0 --- /dev/null +++ b/src/valueObjects/Entry.js @@ -0,0 +1,8 @@ +export function createEntry(path = '', slug = '', raw = '') { + const returnObj = {}; + returnObj.path = path; + returnObj.slug = slug; + returnObj.raw = raw; + returnObj.data = {}; + return returnObj; +}