diff options
Diffstat (limited to 'web')
-rw-r--r-- | web/src/js/components/ContentView.jsx | 71 | ||||
-rw-r--r-- | web/src/js/components/ContentView/ContentLoader.jsx | 8 | ||||
-rw-r--r-- | web/src/js/components/ContentView/ContentViews.jsx | 11 | ||||
-rw-r--r-- | web/src/js/components/FlowView/Messages.jsx | 21 | ||||
-rw-r--r-- | web/src/js/components/FlowView/ToggleEdit.jsx | 7 | ||||
-rw-r--r-- | web/src/js/components/common/CodeEditor.jsx | 31 | ||||
-rw-r--r-- | web/src/js/ducks/flows.js | 5 | ||||
-rw-r--r-- | web/src/js/ducks/ui/flow.js | 14 | ||||
-rw-r--r-- | web/src/js/utils.js | 12 |
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}/> - - <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> - - <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}/> + + <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> + + <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 |