diff --git a/src/actions/config.js b/src/actions/config.js
index f506d705..c0148f3e 100644
--- a/src/actions/config.js
+++ b/src/actions/config.js
@@ -1,7 +1,7 @@
import yaml from 'js-yaml';
import { currentBackend } from '../backends/backend';
import { authenticate } from '../actions/auth';
-import * as ImageProxy from '../valueObjects/ImageProxy';
+import * as MediaProxy from '../valueObjects/MediaProxy';
export const CONFIG_REQUEST = 'CONFIG_REQUEST';
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
@@ -30,7 +30,7 @@ export function configFailed(err) {
export function configDidLoad(config) {
return (dispatch) => {
- ImageProxy.setConfig(config);
+ MediaProxy.setConfig(config);
dispatch(configLoaded(config));
};
}
diff --git a/src/actions/entries.js b/src/actions/entries.js
index 34b806c8..37cd4f1b 100644
--- a/src/actions/entries.js
+++ b/src/actions/entries.js
@@ -14,8 +14,7 @@ export const ENTRIES_FAILURE = 'ENTRIES_FAILURE';
export const DRAFT_CREATE = 'DRAFT_CREATE';
export const DRAFT_DISCARD = 'DRAFT_DISCARD';
export const DRAFT_CHANGE = 'DRAFT_CHANGE';
-export const DRAFT_ADD_MEDIA = 'DRAFT_ADD_MEDIA';
-export const DRAFT_REMOVE_MEDIA = 'DRAFT_REMOVE_MEDIA';
+
export const ENTRY_PERSIST_REQUEST = 'ENTRY_PERSIST_REQUEST';
export const ENTRY_PERSIST_SUCCESS = 'ENTRY_PERSIST_SUCCESS';
@@ -125,20 +124,6 @@ export function changeDraft(entry) {
};
}
-export function addMediaToDraft(mediaFile) {
- return {
- type: DRAFT_ADD_MEDIA,
- payload: mediaFile
- };
-}
-
-export function removeMediaFromDraft(mediaFile) {
- return {
- type: DRAFT_REMOVE_MEDIA,
- payload: mediaFile
- };
-}
-
/*
* Exported Thunk Action Creators
*/
diff --git a/src/actions/media.js b/src/actions/media.js
new file mode 100644
index 00000000..20fc6df8
--- /dev/null
+++ b/src/actions/media.js
@@ -0,0 +1,5 @@
+export const ADD_MEDIA = 'ADD_MEDIA';
+
+export function addMedia(file) {
+ return { type: ADD_MEDIA, payload: file };
+}
diff --git a/src/backends/test-repo/implementation.js b/src/backends/test-repo/implementation.js
index 5493fc6c..4a3a8a32 100644
--- a/src/backends/test-repo/implementation.js
+++ b/src/backends/test-repo/implementation.js
@@ -5,31 +5,6 @@ function getSlug(path) {
return m && m[1];
}
-function getFileData(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = () => {
- resolve(reader.result);
- };
- reader.onerror = function() {
- reject('Unable to read file');
- };
- reader.readAsDataURL(file);
- });
-}
-
-// Only necessary in test-repo, where images won't actually be persisted on server
-function changeFilePathstoBase64(mediaFolder, content, mediaFiles, base64Files) {
- let _content = content;
-
- mediaFiles.forEach((media, index) => {
- const reg = new RegExp('\\b' + mediaFolder + '/' + media.name + '\\b', 'g');
- _content = _content.replace(reg, base64Files[index]);
- });
-
- return _content;
-}
-
export default class TestRepo {
constructor(config) {
this.config = config;
@@ -74,17 +49,9 @@ export default class TestRepo {
}
persist(collection, entry, mediaFiles = []) {
- return new Promise((resolve, reject) => {
- Promise.all(mediaFiles.map((file) => getFileData(file))).then(
- (base64Files) => {
- const content = changeFilePathstoBase64(this.config.get('media_folder'), entry.raw, mediaFiles, base64Files);
- const folder = collection.get('folder');
- const fileName = entry.path.substring(entry.path.lastIndexOf('/') + 1);
- window.repoFiles[folder][fileName]['content'] = content;
- resolve({collection, entry});
- },
- (error) => reject({collection, entry, error})
- );
- });
+ const folder = collection.get('folder');
+ const fileName = entry.path.substring(entry.path.lastIndexOf('/') + 1);
+ window.repoFiles[folder][fileName]['content'] = entry.raw;
+ return Promise.resolve({collection, entry});
}
}
diff --git a/src/components/ControlPane.js b/src/components/ControlPane.js
index ea6f7162..4dacd838 100644
--- a/src/components/ControlPane.js
+++ b/src/components/ControlPane.js
@@ -3,7 +3,7 @@ import Widgets from './Widgets';
export default class ControlPane extends React.Component {
controlFor(field) {
- const { entry, onChange, onAddMedia, onRemoveMedia } = this.props;
+ const { entry, getMedia, onChange, onAddMedia } = this.props;
const widget = Widgets[field.get('widget')] || Widgets._unknown;
return React.createElement(widget.Control, {
key: field.get('name'),
@@ -11,7 +11,7 @@ export default class ControlPane extends React.Component {
value: entry.getIn(['data', field.get('name')]),
onChange: (value) => onChange(entry.setIn(['data', field.get('name')], value)),
onAddMedia: onAddMedia,
- onRemoveMedia: onRemoveMedia
+ getMedia: getMedia
});
}
diff --git a/src/components/EntryEditor.js b/src/components/EntryEditor.js
index 38e6cd3c..7ad3f482 100644
--- a/src/components/EntryEditor.js
+++ b/src/components/EntryEditor.js
@@ -5,7 +5,7 @@ import PreviewPane from './PreviewPane';
export default class EntryEditor extends React.Component {
render() {
- const { collection, entry, onChange, onAddMedia, onRemoveMedia, onPersist } = this.props;
+ const { collection, entry, getMedia, onChange, onAddMedia, onPersist } = this.props;
return
Entry in {collection.get('label')}
{entry && entry.get('title')}
@@ -14,13 +14,13 @@ export default class EntryEditor extends React.Component {
diff --git a/src/components/PreviewPane.js b/src/components/PreviewPane.js
index baca6c86..bce60c92 100644
--- a/src/components/PreviewPane.js
+++ b/src/components/PreviewPane.js
@@ -3,12 +3,13 @@ import Widgets from './Widgets';
export default class PreviewPane extends React.Component {
previewFor(field) {
- const { entry } = this.props;
+ const { entry, getMedia } = this.props;
const widget = Widgets[field.get('widget')] || Widgets._unknown;
return React.createElement(widget.Preview, {
key: field.get('name'),
field: field,
- value: entry.getIn(['data', field.get('name')])
+ value: entry.getIn(['data', field.get('name')]),
+ getMedia: getMedia,
});
}
diff --git a/src/components/Widgets/ImageControl.js b/src/components/Widgets/ImageControl.js
index df8572c2..1e15a0b5 100644
--- a/src/components/Widgets/ImageControl.js
+++ b/src/components/Widgets/ImageControl.js
@@ -1,6 +1,5 @@
import React from 'react';
import { truncateMiddle } from '../../lib/textHelper';
-import ImageProxy from '../../valueObjects/ImageProxy';
const MAX_DISPLAY_LENGTH = 50;
@@ -9,7 +8,7 @@ export default class ImageControl extends React.Component {
super(props);
this.state = {
- currentImage: props.value ? new ImageProxy(props.value, null, true) : null
+ currentImage: props.value
};
this.revokeCurrentImage = this.revokeCurrentImage.bind(this);
@@ -22,8 +21,8 @@ export default class ImageControl extends React.Component {
}
revokeCurrentImage() {
- if (this.state.currentImage && !this.state.currentImage.uploaded) {
- this.props.onRemoveMedia(this.state.currentImage);
+ if (this.state.currentImage) {
+ //this.props.onRemoveMedia(this.state.currentImage);
}
}
@@ -49,7 +48,6 @@ export default class ImageControl extends React.Component {
e.stopPropagation();
e.preventDefault();
this.revokeCurrentImage();
- let imageRef = null;
const fileList = e.dataTransfer ? e.dataTransfer.files : e.target.files;
const files = [...fileList];
const imageType = /^image\//;
@@ -62,17 +60,20 @@ export default class ImageControl extends React.Component {
});
if (file) {
- imageRef = new ImageProxy(file.name, window.URL.createObjectURL(file, {oneTimeOnly: true}));
this.props.onAddMedia(file);
+ this.props.onChange(file.name);
+ this.setState({currentImage: file.name});
+ } else {
+ this.props.onChange(null);
+ this.setState({currentImage: null});
}
- this.props.onChange(imageRef);
- this.setState({currentImage: imageRef});
}
renderImageName() {
+
if (!this.state.currentImage) return null;
- return truncateMiddle(this.state.currentImage.uri, MAX_DISPLAY_LENGTH);
+ return truncateMiddle(this.props.getMedia(this.state.currentImage).uri, MAX_DISPLAY_LENGTH);
}
render() {
diff --git a/src/components/Widgets/ImagePreview.js b/src/components/Widgets/ImagePreview.js
index 06a23cda..8e0a857e 100644
--- a/src/components/Widgets/ImagePreview.js
+++ b/src/components/Widgets/ImagePreview.js
@@ -6,7 +6,7 @@ export default class ImagePreview extends React.Component {
}
render() {
- const { value } = this.props;
- return value ? : null;
+ const { value, getMedia } = this.props;
+ return value ? : null;
}
}
diff --git a/src/containers/CollectionPage.js b/src/containers/CollectionPage.js
index f8e0072f..2311b8d3 100644
--- a/src/containers/CollectionPage.js
+++ b/src/containers/CollectionPage.js
@@ -2,7 +2,7 @@ import React from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import { loadEntries } from '../actions/entries';
-import { selectEntries } from '../reducers/entries';
+import { selectEntries } from '../reducers';
import EntryListing from '../components/EntryListing';
class DashboardPage extends React.Component {
diff --git a/src/containers/EntryPage.js b/src/containers/EntryPage.js
index 123e7040..25e6c1ec 100644
--- a/src/containers/EntryPage.js
+++ b/src/containers/EntryPage.js
@@ -5,11 +5,10 @@ import {
createDraft,
discardDraft,
changeDraft,
- addMediaToDraft,
- removeMediaFromDraft,
persist
} from '../actions/entries';
-import { selectEntry } from '../reducers/entries';
+import { addMedia } from '../actions/media';
+import { selectEntry, getMedia } from '../reducers';
import EntryEditor from '../components/EntryEditor';
class EntryPage extends React.Component {
@@ -40,17 +39,20 @@ class EntryPage extends React.Component {
}
render() {
- const { entry, entryDraft, collection, handleDraftChange, handleDraftAddMedia, handleDraftRemoveMedia } = this.props;
+ const {
+ entry, entryDraft, boundGetMedia, collection, handleDraftChange, handleAddMedia, handleDraftRemoveMedia
+ } = this.props;
+
if (entry == null || entryDraft.get('entry') == undefined || entry.get('isFetching')) {
return Loading...
;
}
return (
);
@@ -62,15 +64,15 @@ function mapStateToProps(state, ownProps) {
const collection = collections.get(ownProps.params.name);
const slug = ownProps.params.slug;
const entry = selectEntry(state, collection.get('name'), slug);
- return {collection, collections, entryDraft, slug, entry};
+ const boundGetMedia = getMedia.bind(null, state);
+ return {collection, collections, entryDraft, boundGetMedia, slug, entry};
}
export default connect(
mapStateToProps,
{
handleDraftChange: changeDraft,
- handleDraftAddMedia: addMediaToDraft,
- handleDraftRemoveMedia: removeMediaFromDraft,
+ handleAddMedia: addMedia,
loadEntry,
createDraft,
discardDraft,
diff --git a/src/formats/yaml.js b/src/formats/yaml.js
index 045d337d..944f3e9e 100644
--- a/src/formats/yaml.js
+++ b/src/formats/yaml.js
@@ -1,6 +1,6 @@
import yaml from 'js-yaml';
import moment from 'moment';
-import ImageProxy from '../valueObjects/ImageProxy';
+import MediaProxy from '../valueObjects/MediaProxy';
const MomentType = new yaml.Type('date', {
kind: 'scalar',
@@ -17,13 +17,13 @@ const MomentType = new yaml.Type('date', {
const ImageType = new yaml.Type('image', {
kind: 'scalar',
- instanceOf: ImageProxy,
+ instanceOf: MediaProxy,
represent: function(value) {
return `${value.uri}`;
},
resolve: function(value) {
if (value === null) return false;
- if (value instanceof ImageProxy) return true;
+ if (value instanceof MediaProxy) return true;
return false;
}
});
diff --git a/src/reducers/auth.js b/src/reducers/auth.js
index 845ec634..69fbdbff 100644
--- a/src/reducers/auth.js
+++ b/src/reducers/auth.js
@@ -1,7 +1,7 @@
import Immutable from 'immutable';
import { AUTH_REQUEST, AUTH_SUCCESS, AUTH_FAILURE } from '../actions/auth';
-export function auth(state = null, action) {
+const auth = (state = null, action) => {
switch (action.type) {
case AUTH_REQUEST:
return Immutable.Map({isFetching: true});
@@ -13,4 +13,6 @@ export function auth(state = null, action) {
default:
return state;
}
-}
+};
+
+export default auth;
diff --git a/src/reducers/collections.js b/src/reducers/collections.js
index 48c24002..ca6e61ce 100644
--- a/src/reducers/collections.js
+++ b/src/reducers/collections.js
@@ -1,7 +1,7 @@
import { OrderedMap, fromJS } from 'immutable';
import { CONFIG_SUCCESS } from '../actions/config';
-export function collections(state = null, action) {
+const collections = (state = null, action) => {
switch (action.type) {
case CONFIG_SUCCESS:
const collections = action.payload && action.payload.collections;
@@ -14,3 +14,5 @@ export function collections(state = null, action) {
return state;
}
}
+
+export default collections;
diff --git a/src/reducers/config.js b/src/reducers/config.js
index c343001a..135f2af1 100644
--- a/src/reducers/config.js
+++ b/src/reducers/config.js
@@ -1,7 +1,7 @@
import Immutable from 'immutable';
import { CONFIG_REQUEST, CONFIG_SUCCESS, CONFIG_FAILURE } from '../actions/config';
-export function config(state = null, action) {
+const config = (state = null, action) => {
switch (action.type) {
case CONFIG_REQUEST:
return Immutable.Map({isFetching: true});
@@ -12,4 +12,6 @@ export function config(state = null, action) {
default:
return state;
}
-}
+};
+
+export default config;
diff --git a/src/reducers/entries.js b/src/reducers/entries.js
index 6c54c39e..ae77ea05 100644
--- a/src/reducers/entries.js
+++ b/src/reducers/entries.js
@@ -3,7 +3,7 @@ import {
ENTRY_REQUEST, ENTRY_SUCCESS, ENTRIES_REQUEST, ENTRIES_SUCCESS
} from '../actions/entries';
-export function entries(state = Map({entities: Map(), pages: Map()}), action) {
+const entries = (state = Map({entities: Map(), pages: Map()}), action) => {
switch (action.type) {
case ENTRY_REQUEST:
return state.setIn(['entities', `${action.payload.collection}.${action.payload.slug}`, 'isFetching'], true);
@@ -28,13 +28,15 @@ export function entries(state = Map({entities: Map(), pages: Map()}), action) {
default:
return state;
}
-}
+};
-export function selectEntry(state, collection, slug) {
- return state.entries.getIn(['entities', `${collection}.${slug}`]);
-}
+export const selectEntry = (state, collection, slug) => (
+ state.getIn(['entities', `${collection}.${slug}`])
+);
-export function selectEntries(state, collection) {
- const slugs = state.entries.getIn(['pages', collection, 'ids']);
+export const selectEntries = (state, collection) => {
+ const slugs = state.getIn(['pages', collection, 'ids']);
return slugs && slugs.map((slug) => selectEntry(state, collection, slug));
-}
+};
+
+export default entries;
diff --git a/src/reducers/entryDraft.js b/src/reducers/entryDraft.js
index cc260c86..7b99298a 100644
--- a/src/reducers/entryDraft.js
+++ b/src/reducers/entryDraft.js
@@ -1,9 +1,10 @@
import { Map, List } from 'immutable';
-import { DRAFT_CREATE, DRAFT_DISCARD, DRAFT_CHANGE, DRAFT_ADD_MEDIA, DRAFT_REMOVE_MEDIA } from '../actions/entries';
+import { DRAFT_CREATE, DRAFT_DISCARD, DRAFT_CHANGE } from '../actions/entries';
+import { ADD_MEDIA } from '../actions/media';
const initialState = Map({entry: Map(), mediaFiles: List()});
-export function entryDraft(state = Map(), action) {
+const entryDraft = (state = Map(), action) => {
switch (action.type) {
case DRAFT_CREATE:
if (!action.payload) {
@@ -20,15 +21,12 @@ export function entryDraft(state = Map(), action) {
case DRAFT_CHANGE:
return state.set('entry', action.payload);
- case DRAFT_ADD_MEDIA:
- return state.update('mediaFiles', (list) => list.push(action.payload));
-
- case DRAFT_REMOVE_MEDIA:
- const mediaIndex = state.get('mediaFiles').indexOf(action.payload);
- if (mediaIndex === -1) return state;
- return state.update('mediaFiles', (list) => list.splice(mediaIndex, 1));
+ case ADD_MEDIA:
+ return state.update('mediaFiles', (list) => list.push(action.payload.name));
default:
return state;
}
-}
+};
+
+export default entryDraft;
diff --git a/src/reducers/index.js b/src/reducers/index.js
new file mode 100644
index 00000000..19f3a266
--- /dev/null
+++ b/src/reducers/index.js
@@ -0,0 +1,27 @@
+import auth from './auth';
+import config from './config';
+import entries, * as fromEntries from './entries';
+import entryDraft from './entryDraft';
+import collections from './collections';
+import medias, * as fromMedias from './medias';
+
+const reducers = {
+ auth,
+ config,
+ collections,
+ entries,
+ entryDraft,
+ medias
+};
+
+export default reducers;
+
+export const selectEntry = (state, collection, slug) =>
+ fromEntries.selectEntry(state.entries, collection, slug);
+
+
+export const selectEntries = (state, collection) =>
+ fromEntries.selectEntries(state.entries, collection);
+
+export const getMedia = (state, fileName) =>
+ fromMedias.getMedia(state.medias, fileName);
diff --git a/src/reducers/medias.js b/src/reducers/medias.js
new file mode 100644
index 00000000..46f11f63
--- /dev/null
+++ b/src/reducers/medias.js
@@ -0,0 +1,23 @@
+import { Map } from 'immutable';
+import { ADD_MEDIA } from '../actions/media';
+import MediaProxy from '../valueObjects/MediaProxy';
+
+const medias = (state = Map(), action) => {
+ switch (action.type) {
+ case ADD_MEDIA:
+ return state.set(action.payload.name, action.payload);
+ default:
+ return state;
+ }
+};
+
+export default medias;
+
+export const getMedia = (state, filePath) => {
+ const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
+ if (state.has(fileName)) {
+ return new MediaProxy(fileName, window.URL.createObjectURL(state.get(fileName), {oneTimeOnly: true}));
+ } else {
+ return new MediaProxy(filePath, null, filePath, true);
+ }
+};
diff --git a/src/store/configureStore.js b/src/store/configureStore.js
index 13784144..3107dba0 100644
--- a/src/store/configureStore.js
+++ b/src/store/configureStore.js
@@ -2,18 +2,10 @@ import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { browserHistory } from 'react-router';
import { syncHistory, routeReducer } from 'react-router-redux';
-import { auth } from '../reducers/auth';
-import { config } from '../reducers/config';
-import { entries } from '../reducers/entries';
-import { entryDraft } from '../reducers/entryDraft';
-import { collections } from '../reducers/collections';
+import reducers from '../reducers';
const reducer = combineReducers({
- auth,
- config,
- collections,
- entries,
- entryDraft,
+ ...reducers,
router: routeReducer
});
diff --git a/src/valueObjects/ImageProxy.js b/src/valueObjects/MediaProxy.js
similarity index 56%
rename from src/valueObjects/ImageProxy.js
rename to src/valueObjects/MediaProxy.js
index 12560bb4..338139eb 100644
--- a/src/valueObjects/ImageProxy.js
+++ b/src/valueObjects/MediaProxy.js
@@ -3,10 +3,10 @@ export const setConfig = (configObj) => {
config = configObj;
};
-export default function ImageProxy(value, objectURL, uploaded = false) {
+export default function MediaProxy(value, objectURL, uri, uploaded = false) {
this.value = value;
this.uploaded = uploaded;
- this.uri = config.media_folder && !uploaded ? config.media_folder + '/' + value : value;
+ this.uri = uri || config.media_folder && config.media_folder + '/' + value;
this.toString = function() {
return uploaded ? this.uri : objectURL;
};