Allow the creation of new entries

This commit is contained in:
Cássio Zen 2016-08-24 21:36:44 -03:00
parent fd79381160
commit b717874e7b
9 changed files with 73 additions and 29 deletions

View File

@ -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();

View File

@ -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);

View File

@ -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) => ({

View File

@ -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));
}
}

View File

@ -11,7 +11,7 @@ export default class StringControl extends React.Component {
}
render() {
return <input value={this.props.value} onChange={this.handleChange}/>;
return <input type="text" value={this.props.value || ''} onChange={this.handleChange}/>;
}
}

View File

@ -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 <div>Loading...</div>;
}
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
}

View File

@ -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:

View File

@ -10,7 +10,8 @@ export default (
<Route path="/" component={App}>
<IndexRoute component={CollectionPage}/>
<Route path="/collections/:name" component={CollectionPage}/>
<Route path="/collections/:name/entries/:slug" component={EntryPage}/>
<Route path="/collections/:name/entries/new" component={EntryPage} newRecord />
<Route path="/collections/:name/entries/:slug" component={EntryPage} />
<Route path="/search" component={SearchPage}/>
<Route path="*" component={NotFoundPage}/>
</Route>

View File

@ -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;
}