aboutsummaryrefslogtreecommitdiffstats
path: root/web/src
diff options
context:
space:
mode:
Diffstat (limited to 'web/src')
-rw-r--r--web/src/js/components/ContentView.jsx71
-rw-r--r--web/src/js/components/ContentView/ContentLoader.jsx8
-rw-r--r--web/src/js/components/ContentView/ContentViews.jsx11
-rw-r--r--web/src/js/components/FlowView/Messages.jsx21
-rw-r--r--web/src/js/components/FlowView/ToggleEdit.jsx7
-rw-r--r--web/src/js/components/common/CodeEditor.jsx31
-rw-r--r--web/src/js/ducks/flows.js5
-rw-r--r--web/src/js/ducks/ui/flow.js14
-rw-r--r--web/src/js/utils.js12
9 files changed, 97 insertions, 83 deletions
diff --git a/web/src/js/components/ContentView.jsx b/web/src/js/components/ContentView.jsx
index f7eafc89..80dec0f4 100644
--- a/web/src/js/components/ContentView.jsx
+++ b/web/src/js/components/ContentView.jsx
@@ -6,7 +6,6 @@ import * as MetaViews from './ContentView/MetaViews'
import ContentLoader from './ContentView/ContentLoader'
import ViewSelector from './ContentView/ViewSelector'
import { setContentView, displayLarge, updateEdit } from '../ducks/ui/flow'
-import CodeEditor from './common/CodeEditor'
ContentView.propTypes = {
// It may seem a bit weird at the first glance:
@@ -19,7 +18,7 @@ ContentView.propTypes = {
ContentView.isContentTooLarge = msg => msg.contentLength > 1024 * 1024 * (ContentViews.ViewImage.matches(msg) ? 10 : 0.2)
function ContentView(props) {
- const { flow, message, contentView, selectView, displayLarge, setDisplayLarge, onContentChange, isFlowEditorOpen, setModifiedFlowContent } = props
+ const { flow, message, contentView, selectView, displayLarge, setDisplayLarge, uploadContent, onContentChange, content, readonly } = props
if (message.contentLength === 0) {
return <MetaViews.ContentEmpty {...props}/>
@@ -34,46 +33,37 @@ function ContentView(props) {
}
const View = ContentViews[contentView]
-
return (
<div>
- {isFlowEditorOpen ? (
- <ContentLoader flow={flow} message={message}>
- <CodeEditor content="" onChange={content =>{setModifiedFlowContent(content)}}/>
- </ContentLoader>
- ): (
- <div>
- {View.textView ? (
- <ContentLoader flow={flow} message={message}>
- <View content="" />
- </ContentLoader>
- ) : (
- <View flow={flow} message={message} />
- )}
- <div className="view-options text-center">
- <ViewSelector onSelectView={selectView} active={View} message={message}/>
- &nbsp;
- <a className="btn btn-default btn-xs"
- href={MessageUtils.getContentURL(flow, message)}
- title="Download the content of the flow.">
- <i className="fa fa-download"/>
- </a>
- &nbsp;
- <a className="btn btn-default btn-xs"
- onClick={() => ContentView.fileInput.click()}
- title="Upload a file to replace the content."
- >
- <i className="fa fa-upload"/>
- </a>
- <input
- ref={ref => ContentView.fileInput = ref}
- className="hidden"
- type="file"
- onChange={e => {if(e.target.files.length > 0) onContentChange(e.target.files[0])}}
- />
- </div>
- </div>
+ {View.textView ? (
+ <ContentLoader flow={flow} readonly={readonly} message={message}>
+ <View readonly={readonly} onChange={onContentChange} content="" />
+ </ContentLoader>
+ ) : (
+ <View flow={flow} readonly={readonly} onChange={onContentChange} content={content} message={message} />
)}
+ <div className="view-options text-center">
+ <ViewSelector onSelectView={selectView} active={View} message={message}/>
+ &nbsp;
+ <a className="btn btn-default btn-xs"
+ href={MessageUtils.getContentURL(flow, message)}
+ title="Download the content of the flow.">
+ <i className="fa fa-download"/>
+ </a>
+ &nbsp;
+ <a className="btn btn-default btn-xs"
+ onClick={() => ContentView.fileInput.click()}
+ title="Upload a file to replace the content."
+ >
+ <i className="fa fa-upload"/>
+ </a>
+ <input
+ ref={ref => ContentView.fileInput = ref}
+ className="hidden"
+ type="file"
+ onChange={e => {if(e.target.files.length > 0) uploadContent(e.target.files[0])}}
+ />
+ </div>
</div>
)
}
@@ -82,11 +72,10 @@ export default connect(
state => ({
contentView: state.ui.flow.contentView,
displayLarge: state.ui.flow.displayLarge,
- isFlowEditorOpen : !!state.ui.flow.modifiedFlow // FIXME
}),
{
selectView: setContentView,
displayLarge,
- updateEdit,
+ updateEdit
}
)(ContentView)
diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx
index 1a23325c..eff82d05 100644
--- a/web/src/js/components/ContentView/ContentLoader.jsx
+++ b/web/src/js/components/ContentView/ContentLoader.jsx
@@ -46,9 +46,13 @@ export default class ContentLoader extends Component {
}
componentWillReceiveProps(nextProps) {
- if (nextProps.message !== this.props.message) {
+ let reload = nextProps.message !== this.props.message
+ let isUserEdit = !nextProps.readonly && nextProps.message.content
+
+ if (isUserEdit)
+ this.setState({content: nextProps.message.content})
+ else if(reload)
this.requestContent(nextProps)
- }
}
componentWillUnmount() {
diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx
index 82ee0adc..b39e545a 100644
--- a/web/src/js/components/ContentView/ContentViews.jsx
+++ b/web/src/js/components/ContentView/ContentViews.jsx
@@ -1,6 +1,7 @@
import React, { PropTypes } from 'react'
import ContentLoader from './ContentLoader'
import { MessageUtils } from '../../flow/utils.js'
+import CodeEditor from '../common/CodeEditor'
const views = [ViewAuto, ViewImage, ViewJSON, ViewRaw]
@@ -28,8 +29,8 @@ ViewRaw.propTypes = {
content: React.PropTypes.string.isRequired,
}
-export function ViewRaw({ content }) {
- return <pre>{content}</pre>
+export function ViewRaw({ content, readonly, onChange }) {
+ return readonly ? <pre>{content}</pre> : <CodeEditor content={content} onChange={onChange}/>
}
ViewJSON.textView = true
@@ -59,12 +60,12 @@ ViewAuto.propTypes = {
flow: React.PropTypes.object.isRequired,
}
-export function ViewAuto({ message, flow }) {
+export function ViewAuto({ message, flow, readonly, onChange }) {
const View = ViewAuto.findView(message)
if (View.textView) {
- return <ContentLoader message={message} flow={flow}><View content="" /></ContentLoader>
+ return <ContentLoader message={message} flow={flow}><View readonly={readonly} onChange={onChange} content="" /></ContentLoader>
} else {
- return <View message={message} flow={flow} />
+ return <View readonly={readonly} message={message} flow={flow} />
}
}
diff --git a/web/src/js/components/FlowView/Messages.jsx b/web/src/js/components/FlowView/Messages.jsx
index 133b2883..9de25b5b 100644
--- a/web/src/js/components/FlowView/Messages.jsx
+++ b/web/src/js/components/FlowView/Messages.jsx
@@ -10,6 +10,7 @@ import ValueEditor from '../ValueEditor/ValueEditor'
import Headers from './Headers'
import { startEdit, updateEdit } from '../../ducks/ui/flow'
+import * as FlowActions from '../../ducks/flows'
import ToggleEdit from './ToggleEdit'
function RequestLine({ flow, readonly, updateFlow }) {
@@ -73,12 +74,13 @@ const Message = connect(
}),
{
updateFlow: updateEdit,
+ uploadContent: FlowActions.uploadContent
}
)
export class Request extends Component {
render() {
- const { flow, isEdit, updateFlow } = this.props
+ const { flow, isEdit, updateFlow, uploadContent } = this.props
return (
<section className="request">
@@ -94,7 +96,12 @@ export class Request extends Component {
/>
<hr/>
- <ContentView flow={flow} message={flow.request}/>
+ <ContentView
+ readonly={!isEdit}
+ flow={flow}
+ onContentChange={content => updateFlow({ request: {content}})}
+ uploadContent={content => uploadContent(flow, content, "request")}
+ message={flow.request}/>
</section>
)
}
@@ -129,7 +136,7 @@ Request = Message(Request)
export class Response extends Component {
render() {
- const { flow, isEdit, updateFlow } = this.props
+ const { flow, isEdit, updateFlow, uploadContent } = this.props
return (
<section className="response">
@@ -144,7 +151,13 @@ export class Response extends Component {
onChange={headers => updateFlow({ response: { headers } })}
/>
<hr/>
- <ContentView flow={flow} message={flow.response}/>
+ <ContentView
+ readonly={!isEdit}
+ flow={flow}
+ onContentChange={content => updateFlow({ response: {content}})}
+ uploadContent={content => uploadContent(flow, content, "response")}
+ message={flow.response}
+ />
</section>
)
}
diff --git a/web/src/js/components/FlowView/ToggleEdit.jsx b/web/src/js/components/FlowView/ToggleEdit.jsx
index 0c8cbbd8..9016348e 100644
--- a/web/src/js/components/FlowView/ToggleEdit.jsx
+++ b/web/src/js/components/FlowView/ToggleEdit.jsx
@@ -10,11 +10,11 @@ ToggleEdit.propTypes = {
stopEdit: PropTypes.func.isRequired,
}
-function ToggleEdit({ isEdit, startEdit, stopEdit, flow }) {
+function ToggleEdit({ isEdit, startEdit, stopEdit, flow, modifiedFlow }) {
return (
<div className="edit-flow-container">
{isEdit ?
- <a className="edit-flow" onClick={() => stopEdit(flow)}>
+ <a className="edit-flow" onClick={() => stopEdit(flow, modifiedFlow)}>
<i className="fa fa-check"/>
</a>
:
@@ -29,7 +29,8 @@ function ToggleEdit({ isEdit, startEdit, stopEdit, flow }) {
export default connect(
state => ({
isEdit: !!state.ui.flow.modifiedFlow,
- flow: state.ui.flow.modifiedFlow || state.flows.byId[state.flows.selected[0]]
+ modifiedFlow: state.ui.flow.modifiedFlow || state.flows.byId[state.flows.selected[0]],
+ flow: state.flows.byId[state.flows.selected[0]]
}),
{
startEdit,
diff --git a/web/src/js/components/common/CodeEditor.jsx b/web/src/js/components/common/CodeEditor.jsx
index 5b2305a8..95f1b98b 100644
--- a/web/src/js/components/common/CodeEditor.jsx
+++ b/web/src/js/components/common/CodeEditor.jsx
@@ -3,28 +3,19 @@ import { render } from 'react-dom';
import Codemirror from 'react-codemirror';
-export default class CodeEditor extends Component{
- static propTypes = {
+CodeEditor.propTypes = {
content: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
- }
-
- constructor(props){
- super(props)
- }
+}
- componentWillMount(){
- this.props.onChange(this.props.content)
- }
+export default function CodeEditor ( { content, onChange} ){
- render() {
- let options = {
- lineNumbers: true
- };
- return (
- <div onKeyDown={e => e.stopPropagation()}>
- <Codemirror value={this.props.content} onChange={this.props.onChange} options={options}/>
- </div>
- )
- }
+ let options = {
+ lineNumbers: true
+ };
+ return (
+ <div onKeyDown={e => e.stopPropagation()}>
+ <Codemirror value={content} onChange={onChange} options={options}/>
+ </div>
+ )
}
diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js
index f18e48e6..f96653a9 100644
--- a/web/src/js/ducks/flows.js
+++ b/web/src/js/ducks/flows.js
@@ -112,10 +112,9 @@ export function update(flow, data) {
return dispatch => fetchApi.put(`/flows/${flow.id}`, data)
}
-export function updateContent(flow, file, type) {
+export function uploadContent(flow, file, type) {
const body = new FormData()
- if (typeof file !== File)
- file = new Blob([file], {type: 'plain/text'})
+ file = new Blob([file], {type: 'plain/text'})
body.append('file', file)
return dispatch => fetchApi(`/flows/${flow.id}/${type}/content`, {method: 'post', body} )
}
diff --git a/web/src/js/ducks/ui/flow.js b/web/src/js/ducks/ui/flow.js
index b1fe535f..100bc771 100644
--- a/web/src/js/ducks/ui/flow.js
+++ b/web/src/js/ducks/ui/flow.js
@@ -1,4 +1,6 @@
import * as flowsActions from '../flows'
+import { getDiff } from "../../utils"
+
import _ from 'lodash'
export const SET_CONTENT_VIEW = 'UI_FLOWVIEW_SET_CONTENT_VIEW',
@@ -6,7 +8,8 @@ export const SET_CONTENT_VIEW = 'UI_FLOWVIEW_SET_CONTENT_VIEW',
SET_TAB = "UI_FLOWVIEW_SET_TAB",
START_EDIT = 'UI_FLOWVIEW_START_EDIT',
UPDATE_EDIT = 'UI_FLOWVIEW_UPDATE_EDIT',
- STOP_EDIT = 'UI_FLOWVIEW_STOP_EDIT'
+ STOP_EDIT = 'UI_FLOWVIEW_STOP_EDIT',
+ UPLOAD_CONTENT = 'UI_FLOWVIEW_UPLOAD_CONTENT'
const defaultState = {
@@ -22,7 +25,7 @@ export default function reducer(state = defaultState, action) {
case START_EDIT:
return {
...state,
- modifiedFlow: action.flow
+ modifiedFlow: action.flow,
}
case UPDATE_EDIT:
@@ -87,10 +90,11 @@ export function updateEdit(update) {
return { type: UPDATE_EDIT, update }
}
-export function stopEdit(flow) {
+export function stopEdit(flow, modified_flow) {
+ let diff = getDiff(flow, modified_flow)
return (dispatch) => {
- dispatch(flowsActions.update(flow, flow)).then(() => {
- dispatch(flowsActions.updateFlow(flow))
+ dispatch(flowsActions.update(flow, diff)).then(() => {
+ dispatch(flowsActions.updateFlow(modified_flow))
dispatch({ type: STOP_EDIT })
})
}
diff --git a/web/src/js/utils.js b/web/src/js/utils.js
index eecacfbb..cc17c565 100644
--- a/web/src/js/utils.js
+++ b/web/src/js/utils.js
@@ -108,6 +108,18 @@ fetchApi.put = (url, json, options) => fetchApi(
}
)
+export function getDiff(obj1, obj2) {
+ let result = {...obj2};
+ for(let key in obj1) {
+ if(_.isEqual(obj2[key], obj1[key]))
+ result[key] = undefined
+ else if(!(Array.isArray(obj2[key]) && Array.isArray(obj1[key])) &&
+ typeof obj2[key] == 'object' && typeof obj1[key] == 'object')
+ result[key] = getDiff(obj1[key], obj2[key])
+ }
+ return result
+}
+
export const pure = renderFn => class extends React.Component {
static displayName = renderFn.name