diff options
author | Maximilian Hils <git@maximilianhils.com> | 2016-07-21 01:14:55 -0700 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2016-07-21 01:14:55 -0700 |
commit | 8a3a21bba1e6706295cc22e1b3a876a7a86cb705 (patch) | |
tree | 9408324d45850fd4def75c60cf3537a12f632217 /web/src/js/components | |
parent | 427fffbcb82ba16dd65a4fee4000a05215e859b8 (diff) | |
download | mitmproxy-8a3a21bba1e6706295cc22e1b3a876a7a86cb705.tar.gz mitmproxy-8a3a21bba1e6706295cc22e1b3a876a7a86cb705.tar.bz2 mitmproxy-8a3a21bba1e6706295cc22e1b3a876a7a86cb705.zip |
web: fix ValueEditor, clean up code
Diffstat (limited to 'web/src/js/components')
-rw-r--r-- | web/src/js/components/ContentView.jsx | 14 | ||||
-rw-r--r-- | web/src/js/components/FlowView.jsx | 4 | ||||
-rw-r--r-- | web/src/js/components/FlowView/FlowEditorButton.jsx | 39 | ||||
-rw-r--r-- | web/src/js/components/FlowView/Headers.jsx | 71 | ||||
-rw-r--r-- | web/src/js/components/FlowView/Messages.jsx | 226 | ||||
-rw-r--r-- | web/src/js/components/FlowView/ToggleEdit.jsx | 38 | ||||
-rw-r--r-- | web/src/js/components/Footer.jsx | 2 | ||||
-rw-r--r-- | web/src/js/components/Header.jsx | 15 | ||||
-rw-r--r-- | web/src/js/components/Header/MainMenu.jsx | 120 | ||||
-rw-r--r-- | web/src/js/components/Header/OptionMenu.jsx | 27 | ||||
-rw-r--r-- | web/src/js/components/MainView.jsx | 18 | ||||
-rw-r--r-- | web/src/js/components/ProxyApp.jsx | 21 | ||||
-rwxr-xr-x | web/src/js/components/ValueEditor.jsx | 26 | ||||
-rwxr-xr-x | web/src/js/components/ValueEditor/ValidateEditor.jsx | 44 | ||||
-rw-r--r--[-rwxr-xr-x] | web/src/js/components/ValueEditor/ValueEditor.jsx (renamed from web/src/js/components/ValueEditor/EditorBase.jsx) | 89 |
15 files changed, 347 insertions, 407 deletions
diff --git a/web/src/js/components/ContentView.jsx b/web/src/js/components/ContentView.jsx index 766de26f..f7eafc89 100644 --- a/web/src/js/components/ContentView.jsx +++ b/web/src/js/components/ContentView.jsx @@ -5,7 +5,7 @@ import * as ContentViews from './ContentView/ContentViews' import * as MetaViews from './ContentView/MetaViews' import ContentLoader from './ContentView/ContentLoader' import ViewSelector from './ContentView/ViewSelector' -import { setContentView, setDisplayLarge, setModifiedFlowContent } from '../ducks/ui' +import { setContentView, displayLarge, updateEdit } from '../ducks/ui/flow' import CodeEditor from './common/CodeEditor' ContentView.propTypes = { @@ -30,7 +30,7 @@ function ContentView(props) { } if (!displayLarge && ContentView.isContentTooLarge(message)) { - return <MetaViews.ContentTooLarge {...props} onClick={() => setDisplayLarge(true)}/> + return <MetaViews.ContentTooLarge {...props} onClick={displayLarge}/> } const View = ContentViews[contentView] @@ -80,13 +80,13 @@ function ContentView(props) { export default connect( state => ({ - contentView: state.ui.contentView, - displayLarge: state.ui.displayLarge, - isFlowEditorOpen : state.ui.isFlowEditorOpen + contentView: state.ui.flow.contentView, + displayLarge: state.ui.flow.displayLarge, + isFlowEditorOpen : !!state.ui.flow.modifiedFlow // FIXME }), { selectView: setContentView, - setDisplayLarge, - setModifiedFlowContent + displayLarge, + updateEdit, } )(ContentView) diff --git a/web/src/js/components/FlowView.jsx b/web/src/js/components/FlowView.jsx index 5ba472a5..a80dc040 100644 --- a/web/src/js/components/FlowView.jsx +++ b/web/src/js/components/FlowView.jsx @@ -7,7 +7,7 @@ import { Request, Response, ErrorView as Error } from './FlowView/Messages' import Details from './FlowView/Details' import Prompt from './Prompt' -import { setPrompt, selectTab } from '../ducks/ui' +import { selectTab } from '../ducks/ui/flow' export default class FlowView extends Component { @@ -90,9 +90,9 @@ export default class FlowView extends Component { export default connect( state => ({ promptOpen: state.ui.promptOpen, + tab: state.ui.flow.tab }), { - setPrompt, selectTab, } )(FlowView) diff --git a/web/src/js/components/FlowView/FlowEditorButton.jsx b/web/src/js/components/FlowView/FlowEditorButton.jsx deleted file mode 100644 index 3d0d1d16..00000000 --- a/web/src/js/components/FlowView/FlowEditorButton.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { PropTypes, Component } from 'react' -import { connect } from 'react-redux' - -import {closeFlowEditor} from '../../ducks/ui.js' -import {openFlowEditor} from '../../ducks/ui.js' - -FlowEditorButton.propTypes = { - isFlowEditorOpen: PropTypes.bool.isRequired, - content: PropTypes.string.isRequired, - onContentChange: PropTypes.func.isRequired -} - -function FlowEditorButton ({ isFlowEditorOpen, closeFlowEditor, openFlowEditor, onContentChange, content }) { - return ( - <div className="edit-flow-container"> - {isFlowEditorOpen ? - <a className="edit-flow" onClick={() => {onContentChange(content); closeFlowEditor()}}> - <i className="fa fa-check"/> - </a> - : - <a className="edit-flow" onClick={() => openFlowEditor()}> - <i className="fa fa-pencil"/> - </a> - } - </div> - ) -} - -export default connect( - state => ({ - isFlowEditorOpen: state.ui.isFlowEditorOpen, - content: state.ui.modifiedFlow.content - }), - { - closeFlowEditor, - openFlowEditor - - } -)(FlowEditorButton) diff --git a/web/src/js/components/FlowView/Headers.jsx b/web/src/js/components/FlowView/Headers.jsx index 880eeda1..706dd404 100644 --- a/web/src/js/components/FlowView/Headers.jsx +++ b/web/src/js/components/FlowView/Headers.jsx @@ -1,12 +1,21 @@ import React, { Component, PropTypes } from 'react' import ReactDOM from 'react-dom' -import ValueEditor from '../ValueEditor' -import { Key } from '../../utils.js' +import ValueEditor from '../ValueEditor/ValueEditor' +import { Key } from '../../utils' class HeaderEditor extends Component { + constructor(props) { + super(props) + this.onKeyDown = this.onKeyDown.bind(this) + } + render() { - return <ValueEditor ref="input" {...this.props} onKeyDown={this.onKeyDown} inline/> + let { onTab, ...props } = this.props + return <ValueEditor + {...props} + onKeyDown={this.onKeyDown} + /> } focus() { @@ -21,6 +30,7 @@ class HeaderEditor extends Component { this.props.onRemove(e) } break + case Key.ENTER: case Key.TAB: if (!e.shiftKey) { this.props.onTab(e) @@ -66,7 +76,12 @@ export default class Headers extends Component { onTab(row, col, e) { const headers = this.props.message.headers - if (row !== headers.length - 1 || col !== 1) { + if (col === 0) { + this._nextSel = `${row}-value` + return + } + if (row !== headers.length - 1) { + this._nextSel = `${row + 1}-key` return } @@ -96,33 +111,35 @@ export default class Headers extends Component { } render() { - const { message } = this.props + const { message, readonly } = this.props return ( <table className="header-table"> <tbody> - {message.headers.map((header, i) => ( - <tr key={i}> - <td className="header-name"> - <HeaderEditor - ref={`${i}-key`} - content={header[0]} - onDone={val => this.onChange(i, 0, val)} - onRemove={event => this.onRemove(i, 0, event)} - onTab={event => this.onTab(i, 0, event)} - />: - </td> - <td className="header-value"> - <HeaderEditor - ref={`${i}-value`} - content={header[1]} - onDone={val => this.onChange(i, 1, val)} - onRemove={event => this.onRemove(i, 1, event)} - onTab={event => this.onTab(i, 1, event)} - /> - </td> - </tr> - ))} + {message.headers.map((header, i) => ( + <tr key={i}> + <td className="header-name"> + <HeaderEditor + ref={`${i}-key`} + content={header[0]} + readonly={readonly} + onDone={val => this.onChange(i, 0, val)} + onRemove={event => this.onRemove(i, 0, event)} + onTab={event => this.onTab(i, 0, event)} + />: + </td> + <td className="header-value"> + <HeaderEditor + ref={`${i}-value`} + content={header[1]} + readonly={readonly} + onDone={val => this.onChange(i, 1, val)} + onRemove={event => this.onRemove(i, 1, event)} + onTab={event => this.onTab(i, 1, event)} + /> + </td> + </tr> + ))} </tbody> </table> ) diff --git a/web/src/js/components/FlowView/Messages.jsx b/web/src/js/components/FlowView/Messages.jsx index 2f03c712..133b2883 100644 --- a/web/src/js/components/FlowView/Messages.jsx +++ b/web/src/js/components/FlowView/Messages.jsx @@ -1,172 +1,180 @@ import React, { Component, PropTypes } from 'react' -import _ from 'lodash' - -import { RequestUtils, isValidHttpVersion, parseUrl, parseHttpVersion } from '../../flow/utils.js' -import { Key, formatTimeStamp } from '../../utils.js' -import ContentView from '../ContentView' -import ValueEditor from '../ValueEditor' -import Headers from './Headers' -import * as flowActions from '../../ducks/flows' -import FlowEditorButton from './FlowEditorButton' +import { connect } from 'react-redux' -class RequestLine extends Component { +import { RequestUtils, isValidHttpVersion, parseUrl } from '../../flow/utils.js' +import { formatTimeStamp } from '../../utils.js' +import ContentView from '../ContentView' +import ValidateEditor from '../ValueEditor/ValidateEditor' +import ValueEditor from '../ValueEditor/ValueEditor' - render() { - const { flow, updateFlow } = this.props +import Headers from './Headers' +import { startEdit, updateEdit } from '../../ducks/ui/flow' +import ToggleEdit from './ToggleEdit' - return ( - <div className="first-line request-line"> +function RequestLine({ flow, readonly, updateFlow }) { + return ( + <div className="first-line request-line"> + <div> <ValueEditor - ref="method" content={flow.request.method} + readonly={readonly} onDone={method => updateFlow({ request: { method } })} - inline /> - <ValueEditor - ref="url" + <ValidateEditor content={RequestUtils.pretty_url(flow.request)} - onDone={url => updateFlow({ request: Object.assign({ path: '' }, parseUrl(url)) })} + readonly={readonly} + onDone={url => updateFlow({ request: {path: '', ...parseUrl(url)}})} isValid={url => !!parseUrl(url).host} - inline /> - <ValueEditor - ref="httpVersion" + <ValidateEditor content={flow.request.http_version} - onDone={ver => updateFlow({ request: { http_version: parseHttpVersion(ver) } })} + readonly={readonly} + onDone={http_version => updateFlow({ request: { http_version } })} isValid={isValidHttpVersion} - inline /> </div> - ) - } + </div> + ) } -class ResponseLine extends Component { - - render() { - const { flow, updateFlow } = this.props +function ResponseLine({ flow, readonly, updateFlow }) { + return ( + <div className="first-line response-line"> + <ValidateEditor + content={flow.response.http_version} + readonly={readonly} + onDone={nextVer => updateFlow({ response: { http_version: nextVer } })} + isValid={isValidHttpVersion} + /> + + <ValidateEditor + content={flow.response.status_code + ''} + readonly={readonly} + onDone={code => updateFlow({ response: { code: parseInt(code) } })} + isValid={code => /^\d+$/.test(code)} + /> + + <ValueEditor + content={flow.response.reason} + readonly={readonly} + onDone={msg => updateFlow({ response: { msg } })} + /> + </div> + ) +} - return ( - <div className="first-line response-line"> - <ValueEditor - ref="httpVersion" - content={flow.response.http_version} - onDone={nextVer => updateFlow({ response: { http_version: parseHttpVersion(nextVer) } })} - isValid={isValidHttpVersion} - inline - /> - - <ValueEditor - ref="code" - content={flow.response.status_code + ''} - onDone={code => updateFlow({ response: { code: parseInt(code) } })} - isValid={code => /^\d+$/.test(code)} - inline - /> - - <ValueEditor - ref="msg" - content={flow.response.reason} - onDone={msg => updateFlow({ response: { msg } })} - inline - /> - </div> - ) +const Message = connect( + state => ({ + flow: state.ui.flow.modifiedFlow || state.flows.byId[state.flows.selected[0]], + isEdit: !!state.ui.flow.modifiedFlow, + }), + { + updateFlow: updateEdit, } -} +) export class Request extends Component { - render() { - const { flow, updateFlow } = this.props - let onContentChange = content => flowActions.updateContent(this.props.flow, content, "request") + render() { + const { flow, isEdit, updateFlow } = this.props return ( <section className="request"> - <FlowEditorButton onContentChange={onContentChange}/> - <RequestLine ref="requestLine" flow={flow} updateFlow={updateFlow} /> + <ToggleEdit/> + <RequestLine + flow={flow} + readonly={!isEdit} + updateFlow={updateFlow}/> <Headers - ref="headers" message={flow.request} + readonly={!isEdit} onChange={headers => updateFlow({ request: { headers } })} /> <hr/> - <ContentView flow={flow} - onContentChange={onContentChange} - message={flow.request} - /> + <ContentView flow={flow} message={flow.request}/> </section> ) } + edit(k) { - switch (k) { - case 'm': - this.refs.requestLine.refs.method.focus() - break - case 'u': - this.refs.requestLine.refs.url.focus() - break - case 'v': - this.refs.requestLine.refs.httpVersion.focus() - break - case 'h': - this.refs.headers.edit() - break - default: - throw new Error(`Unimplemented: ${k}`) - } + throw "unimplemented" + /* + switch (k) { + case 'm': + this.refs.requestLine.refs.method.focus() + break + case 'u': + this.refs.requestLine.refs.url.focus() + break + case 'v': + this.refs.requestLine.refs.httpVersion.focus() + break + case 'h': + this.refs.headers.edit() + break + default: + throw new Error(`Unimplemented: ${k}`) + } + */ } + } -export class Response extends Component { +Request = Message(Request) +export class Response extends Component { render() { - const { flow, updateFlow } = this.props - let onContentChange = content => flowActions.updateContent(this.props.flow, content, "response") + const { flow, isEdit, updateFlow } = this.props return ( <section className="response"> - <FlowEditorButton onContentChange={onContentChange}/> - <ResponseLine ref="responseLine" flow={flow} updateFlow={updateFlow} /> + <ToggleEdit/> + <ResponseLine + flow={flow} + readonly={!isEdit} + updateFlow={updateFlow}/> <Headers - ref="headers" message={flow.response} + readonly={!isEdit} onChange={headers => updateFlow({ response: { headers } })} /> <hr/> - <ContentView flow={flow} - onContentChange={onContentChange} - message={flow.response} - /> + <ContentView flow={flow} message={flow.response}/> </section> ) } edit(k) { - switch (k) { - case 'c': - this.refs.responseLine.refs.status_code.focus() - break - case 'm': - this.refs.responseLine.refs.msg.focus() - break - case 'v': - this.refs.responseLine.refs.httpVersion.focus() - break - case 'h': - this.refs.headers.edit() - break - default: - throw new Error(`'Unimplemented: ${k}`) - } + throw "unimplemented" + /* + switch (k) { + case 'c': + this.refs.responseLine.refs.status_code.focus() + break + case 'm': + this.refs.responseLine.refs.msg.focus() + break + case 'v': + this.refs.responseLine.refs.httpVersion.focus() + break + case 'h': + this.refs.headers.edit() + break + default: + throw new Error(`'Unimplemented: ${k}`) + } + */ } } +Response = Message(Response) + + ErrorView.propTypes = { flow: PropTypes.object.isRequired, } diff --git a/web/src/js/components/FlowView/ToggleEdit.jsx b/web/src/js/components/FlowView/ToggleEdit.jsx new file mode 100644 index 00000000..0c8cbbd8 --- /dev/null +++ b/web/src/js/components/FlowView/ToggleEdit.jsx @@ -0,0 +1,38 @@ +import React, { PropTypes, Component } from 'react' +import { connect } from 'react-redux' + +import { startEdit, stopEdit } from '../../ducks/ui/flow' + +ToggleEdit.propTypes = { + isEdit: PropTypes.bool.isRequired, + flow: PropTypes.object.isRequired, + startEdit: PropTypes.func.isRequired, + stopEdit: PropTypes.func.isRequired, +} + +function ToggleEdit({ isEdit, startEdit, stopEdit, flow }) { + return ( + <div className="edit-flow-container"> + {isEdit ? + <a className="edit-flow" onClick={() => stopEdit(flow)}> + <i className="fa fa-check"/> + </a> + : + <a className="edit-flow" onClick={() => startEdit(flow)}> + <i className="fa fa-pencil"/> + </a> + } + </div> + ) +} + +export default connect( + state => ({ + isEdit: !!state.ui.flow.modifiedFlow, + flow: state.ui.flow.modifiedFlow || state.flows.byId[state.flows.selected[0]] + }), + { + startEdit, + stopEdit, + } +)(ToggleEdit) diff --git a/web/src/js/components/Footer.jsx b/web/src/js/components/Footer.jsx index 82d6d8a1..2bda70e1 100644 --- a/web/src/js/components/Footer.jsx +++ b/web/src/js/components/Footer.jsx @@ -48,6 +48,6 @@ function Footer({ settings }) { export default connect( state => ({ - settings: state.settings.settings, + settings: state.settings, }) )(Footer) diff --git a/web/src/js/components/Header.jsx b/web/src/js/components/Header.jsx index 5de885ae..702786e6 100644 --- a/web/src/js/components/Header.jsx +++ b/web/src/js/components/Header.jsx @@ -6,7 +6,7 @@ import ViewMenu from './Header/ViewMenu' import OptionMenu from './Header/OptionMenu' import FileMenu from './Header/FileMenu' import FlowMenu from './Header/FlowMenu' -import {setActiveMenu} from '../ducks/ui.js' +import {setActiveMenu} from '../ducks/ui/header' class Header extends Component { static entries = [MainMenu, ViewMenu, OptionMenu] @@ -17,7 +17,7 @@ class Header extends Component { } render() { - const { query, selectedFlowId, activeMenu} = this.props + const { selectedFlowId, activeMenu} = this.props let entries = [...Header.entries] if(selectedFlowId) @@ -39,10 +39,7 @@ class Header extends Component { ))} </nav> <div className="menu"> - <Active - ref="active" - query={query} - /> + <Active/> </div> </header> ) @@ -52,13 +49,9 @@ class Header extends Component { export default connect( state => ({ selectedFlowId: state.flows.selected[0], - activeMenu: state.ui.activeMenu, + activeMenu: state.ui.header.activeMenu, }), { setActiveMenu, - }, - null, - { - withRef: true, } )(Header) diff --git a/web/src/js/components/Header/MainMenu.jsx b/web/src/js/components/Header/MainMenu.jsx index 27a4be60..7236d31f 100644 --- a/web/src/js/components/Header/MainMenu.jsx +++ b/web/src/js/components/Header/MainMenu.jsx @@ -1,92 +1,50 @@ import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' import FilterInput from './FilterInput' -import { Query } from '../../actions.js' import { update as updateSettings } from '../../ducks/settings' -import { updateQuery, setSelectedInput } from '../../ducks/ui' +import { updateFilter, updateHighlight } from '../../ducks/flowView' -class MainMenu extends Component { +MainMenu.title = "Start" - static title = 'Start' - static route = 'flows' - - static propTypes = { - query: PropTypes.object.isRequired, - settings: PropTypes.object.isRequired, - updateSettings: PropTypes.func.isRequired, - updateQuery: PropTypes.func.isRequired, - } - - constructor(props, context) { - super(props, context) - this.onSearchChange = this.onSearchChange.bind(this) - this.onHighlightChange = this.onHighlightChange.bind(this) - } - - componentWillReceiveProps(nextProps) { - if(this.refs[nextProps.selectedInput]) { - this.refs[nextProps.selectedInput].select() - } - this.props.setSelectedInput(undefined) - } - - onSearchChange(val) { - this.props.updateQuery({ [Query.SEARCH]: val }) - } - - onHighlightChange(val) { - this.props.updateQuery({ [Query.HIGHLIGHT]: val }) - } - - render() { - const { query, settings, updateSettings } = this.props - - return ( - <div> - <div className="menu-row"> - <FilterInput - ref="search" - placeholder="Search" - type="search" - color="black" - value={query[Query.SEARCH] || ''} - onChange={this.onSearchChange} - /> - <FilterInput - ref="highlight" - placeholder="Highlight" - type="tag" - color="hsl(48, 100%, 50%)" - value={query[Query.HIGHLIGHT] || ''} - onChange={this.onHighlightChange} - /> - <FilterInput - ref="intercept" - placeholder="Intercept" - type="pause" - color="hsl(208, 56%, 53%)" - value={settings.intercept || ''} - onChange={intercept => updateSettings({ intercept })} - /> - </div> - <div className="clearfix"></div> +export default function MainMenu() { + return ( + <div> + <div className="menu-row"> + <FlowFilterInput/> + <HighlightInput/> + <InterceptInput/> </div> - ) - } + <div className="clearfix"></div> + </div> + ) } -export default connect( +const InterceptInput = connect( + state => ({ + value: state.settings.intercept || '', + placeholder: 'Intercept', + type: 'pause', + color: 'hsl(208, 56%, 53%)' + }), + { onChange: intercept => updateSettings({ intercept }) } +)(FilterInput); + +const FlowFilterInput = connect( + state => ({ + value: state.flowView.filter || '', + placeholder: 'Search', + type: 'search', + color: 'black' + }), + { onChange: updateFilter } +)(FilterInput); + +const HighlightInput = connect( state => ({ - settings: state.settings.settings, - selectedInput: state.ui.selectedInput + value: state.flowView.highlight || '', + placeholder: 'Highlight', + type: 'tag', + color: 'hsl(48, 100%, 50%)' }), - { - updateSettings, - updateQuery, - setSelectedInput - }, - null, - { - withRef: true, - } -)(MainMenu); + { onChange: updateHighlight } +)(FilterInput); diff --git a/web/src/js/components/Header/OptionMenu.jsx b/web/src/js/components/Header/OptionMenu.jsx index 4a487cc9..a338fed0 100644 --- a/web/src/js/components/Header/OptionMenu.jsx +++ b/web/src/js/components/Header/OptionMenu.jsx @@ -8,53 +8,52 @@ OptionMenu.title = 'Options' OptionMenu.propTypes = { settings: PropTypes.object.isRequired, - onSettingsChange: PropTypes.func.isRequired, + updateSettings: PropTypes.func.isRequired, } -function OptionMenu({ settings, onSettingsChange }) { - // @todo use settings.map +function OptionMenu({ settings, updateSettings }) { return ( <div> <div className="menu-row"> <ToggleButton text="showhost" checked={settings.showhost} - onToggle={() => onSettingsChange({ showhost: !settings.showhost })} + onToggle={() => updateSettings({ showhost: !settings.showhost })} /> <ToggleButton text="no_upstream_cert" checked={settings.no_upstream_cert} - onToggle={() => onSettingsChange({ no_upstream_cert: !settings.no_upstream_cert })} + onToggle={() => updateSettings({ no_upstream_cert: !settings.no_upstream_cert })} /> <ToggleButton text="rawtcp" checked={settings.rawtcp} - onToggle={() => onSettingsChange({ rawtcp: !settings.rawtcp })} + onToggle={() => updateSettings({ rawtcp: !settings.rawtcp })} /> <ToggleButton text="http2" checked={settings.http2} - onToggle={() => onSettingsChange({ http2: !settings.http2 })} + onToggle={() => updateSettings({ http2: !settings.http2 })} /> <ToggleButton text="anticache" checked={settings.anticache} - onToggle={() => onSettingsChange({ anticache: !settings.anticache })} + onToggle={() => updateSettings({ anticache: !settings.anticache })} /> <ToggleButton text="anticomp" checked={settings.anticomp} - onToggle={() => onSettingsChange({ anticomp: !settings.anticomp })} + onToggle={() => updateSettings({ anticomp: !settings.anticomp })} /> <ToggleInputButton name="stickyauth" placeholder="Sticky auth filter" checked={!!settings.stickyauth} txt={settings.stickyauth || ''} - onToggleChanged={txt => onSettingsChange({ stickyauth: !settings.stickyauth ? txt : null })} + onToggleChanged={txt => updateSettings({ stickyauth: !settings.stickyauth ? txt : null })} /> <ToggleInputButton name="stickycookie" placeholder="Sticky cookie filter" checked={!!settings.stickycookie} txt={settings.stickycookie || ''} - onToggleChanged={txt => onSettingsChange({ stickycookie: !settings.stickycookie ? txt : null })} + onToggleChanged={txt => updateSettings({ stickycookie: !settings.stickycookie ? txt : null })} /> <ToggleInputButton name="stream" placeholder="stream..." checked={!!settings.stream} txt={settings.stream || ''} inputType="number" - onToggleChanged={txt => onSettingsChange({ stream: !settings.stream ? txt : null })} + onToggleChanged={txt => updateSettings({ stream: !settings.stream ? txt : null })} /> </div> <div className="clearfix"/> @@ -64,9 +63,9 @@ function OptionMenu({ settings, onSettingsChange }) { export default connect( state => ({ - settings: state.settings.settings, + settings: state.settings, }), { - onSettingsChange: updateSettings, + updateSettings, } )(OptionMenu) diff --git a/web/src/js/components/MainView.jsx b/web/src/js/components/MainView.jsx index b0bbf70e..d7d1ebeb 100644 --- a/web/src/js/components/MainView.jsx +++ b/web/src/js/components/MainView.jsx @@ -1,6 +1,5 @@ import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' -import { Query } from '../actions.js' import Splitter from './common/Splitter' import FlowTable from './FlowTable' import FlowView from './FlowView' @@ -14,19 +13,6 @@ class MainView extends Component { sort: PropTypes.object, } - /** - * @todo move to actions - * @todo replace with mapStateToProps - */ - componentWillReceiveProps(nextProps) { - if (nextProps.location.query[Query.SEARCH] !== nextProps.filter) { - this.props.updateFilter(nextProps.location.query[Query.SEARCH], false) - } - if (nextProps.location.query[Query.HIGHLIGHT] !== nextProps.highlight) { - this.props.updateHighlight(nextProps.location.query[Query.HIGHLIGHT], false) - } - } - render() { const { flows, selectedFlow, highlight } = this.props return ( @@ -66,9 +52,5 @@ export default connect( updateFilter, updateHighlight, updateFlow: flowsActions.update, - }, - undefined, - { - withRef: true } )(MainView) diff --git a/web/src/js/components/ProxyApp.jsx b/web/src/js/components/ProxyApp.jsx index 2962fc2b..f8a6e262 100644 --- a/web/src/js/components/ProxyApp.jsx +++ b/web/src/js/components/ProxyApp.jsx @@ -1,14 +1,11 @@ import React, { Component, PropTypes } from 'react' -import ReactDOM from 'react-dom' -import _ from 'lodash' import { connect } from 'react-redux' import { init as appInit, destruct as appDestruct } from '../ducks/app' -import { onKeyDown } from '../ducks/ui' +import { onKeyDown } from '../ducks/ui/keyboard' import Header from './Header' import EventLog from './EventLog' import Footer from './Footer' -import { Key } from '../utils.js' class ProxyAppMain extends Component { @@ -27,6 +24,15 @@ class ProxyAppMain extends Component { } componentWillReceiveProps(nextProps) { + /* + FIXME: improve react-router -> redux integration. + if (nextProps.location.query[Query.SEARCH] !== nextProps.filter) { + this.props.updateFilter(nextProps.location.query[Query.SEARCH], false) + } + if (nextProps.location.query[Query.HIGHLIGHT] !== nextProps.highlight) { + this.props.updateHighlight(nextProps.location.query[Query.HIGHLIGHT], false) + } + */ if (nextProps.query === this.props.query && nextProps.selectedFlowId === this.props.selectedFlowId && nextProps.panel === this.props.panel) { return } @@ -35,13 +41,14 @@ class ProxyAppMain extends Component { } else { this.context.router.replace({ pathname: '/flows', query: nextProps.query }) } + } render() { const { showEventLog, location, children, query } = this.props return ( <div id="container" tabIndex="0"> - <Header ref="header" query={query} /> + <Header/> {React.cloneElement( children, { ref: 'view', location, query } @@ -58,8 +65,8 @@ class ProxyAppMain extends Component { export default connect( state => ({ showEventLog: state.eventLog.visible, - query: state.ui.query, - panel: state.ui.panel, + query: state.flowView.filter, + panel: state.ui.flow.tab, selectedFlowId: state.flows.selected[0] }), { diff --git a/web/src/js/components/ValueEditor.jsx b/web/src/js/components/ValueEditor.jsx deleted file mode 100755 index 5f1bf2dc..00000000 --- a/web/src/js/components/ValueEditor.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { Component, PropTypes } from 'react' -import ReactDOM from 'react-dom' -import ValidateEditor from './ValueEditor/ValidateEditor' - -export default class ValueEditor extends Component { - - static propTypes = { - content: PropTypes.string.isRequired, - onDone: PropTypes.func.isRequired, - inline: PropTypes.bool, - } - - render() { - var tag = this.props.inline ? 'span' : 'div' - return ( - <ValidateEditor - {...this.props} - tag={tag} - /> - ) - } - - focus() { - ReactDOM.findDOMNode(this).focus(); - } -} diff --git a/web/src/js/components/ValueEditor/ValidateEditor.jsx b/web/src/js/components/ValueEditor/ValidateEditor.jsx index 2f362986..7415c1b8 100755 --- a/web/src/js/components/ValueEditor/ValidateEditor.jsx +++ b/web/src/js/components/ValueEditor/ValidateEditor.jsx @@ -1,57 +1,57 @@ import React, { Component, PropTypes } from 'react' -import ReactDOM from 'react-dom' -import EditorBase from './EditorBase' +import ValueEditor from './ValueEditor' +import classnames from 'classnames' + export default class ValidateEditor extends Component { static propTypes = { content: PropTypes.string.isRequired, + readonly: PropTypes.bool, onDone: PropTypes.func.isRequired, - onInput: PropTypes.func, - isValid: PropTypes.func, className: PropTypes.string, + isValid: PropTypes.func.isRequired, } constructor(props) { super(props) - this.state = { currentContent: props.content } + this.state = { valid: props.isValid(props.content) } this.onInput = this.onInput.bind(this) this.onDone = this.onDone.bind(this) } componentWillReceiveProps(nextProps) { - this.setState({ currentContent: nextProps.content }) + this.setState({ valid: nextProps.isValid(nextProps.content) }) } - onInput(currentContent) { - this.setState({ currentContent }) - this.props.onInput && this.props.onInput(currentContent) + onInput(content) { + this.setState({ valid: this.props.isValid(content) }) } onDone(content) { - if (this.props.isValid && !this.props.isValid(content)) { - this.refs.editor.reset() + if (!this.props.isValid(content)) { + this.editor.reset() content = this.props.content } this.props.onDone(content) } render() { - let className = this.props.className || '' - if (this.props.isValid) { - if (this.props.isValid(this.state.currentContent)) { - className += ' has-success' - } else { - className += ' has-warning' + let className = classnames( + this.props.className, + { + 'has-success': this.state.valid, + 'has-warning': !this.state.valid } - } + ) return ( - <EditorBase - {...this.props} - ref="editor" - className={className} + <ValueEditor + content={this.props.content} + readonly={this.props.readonly} onDone={this.onDone} onInput={this.onInput} + className={className} + ref={(e) => this.editor = e} /> ) } diff --git a/web/src/js/components/ValueEditor/EditorBase.jsx b/web/src/js/components/ValueEditor/ValueEditor.jsx index aa09dad5..dd9c2cde 100755..100644 --- a/web/src/js/components/ValueEditor/EditorBase.jsx +++ b/web/src/js/components/ValueEditor/ValueEditor.jsx @@ -1,61 +1,66 @@ import React, { Component, PropTypes } from 'react' -import ReactDOM from 'react-dom' -import {Key} from '../../utils.js' +import _ from "lodash" +import classnames from 'classnames' -export default class EditorBase extends Component { +import { Key } from '../../utils' + +export default class ValueEditor extends Component { static propTypes = { content: PropTypes.string.isRequired, + readonly: PropTypes.bool, onDone: PropTypes.func.isRequired, - contentToHtml: PropTypes.func, - nodeToContent: PropTypes.func, - onStop: PropTypes.func, - submitOnEnter: PropTypes.bool, className: PropTypes.string, - tag: PropTypes.string, + onInput: PropTypes.func, + onKeyDown: PropTypes.func, } static defaultProps = { - contentToHtml: content => _.escape(content), - nodeToContent: node => node.textContent, - submitOnEnter: true, - className: '', - tag: 'div', - onStop: _.noop, - onMouseDown: _.noop, - onBlur: _.noop, - onInput: _.noop, + onInput: () => {}, + onKeyDown: () => {}, } constructor(props) { super(props) - this.state = {editable: false} + this.state = { editable: false } this.onPaste = this.onPaste.bind(this) this.onMouseDown = this.onMouseDown.bind(this) this.onMouseUp = this.onMouseUp.bind(this) this.onFocus = this.onFocus.bind(this) this.onClick = this.onClick.bind(this) - this.stop = this.stop.bind(this) + this.blur = this.blur.bind(this) this.onBlur = this.onBlur.bind(this) this.reset = this.reset.bind(this) this.onKeyDown = this.onKeyDown.bind(this) this.onInput = this.onInput.bind(this) } - stop() { + blur() { // a stop would cause a blur as a side-effect. // but a blur event must trigger a stop as well. // to fix this, make stop = blur and do the actual stop in the onBlur handler. - ReactDOM.findDOMNode(this).blur() - this.props.onStop() + this.input.blur() + } + + reset() { + this.input.innerHTML = _.escape(this.props.content) } render() { + let className = classnames( + 'inline-input', + { + 'readonly': this.props.readonly, + 'editable': !this.props.readonly + }, + this.props.className + ) return ( - <this.props.tag - tabIndex="0" - className={`inline-input ${this.props.className}`} + <div + ref={input => this.input = input} + tabIndex={!this.props.readonly && "0"} + className={className} contentEditable={this.state.editable || undefined} onFocus={this.onFocus} onMouseDown={this.onMouseDown} @@ -64,8 +69,8 @@ export default class EditorBase extends Component { onKeyDown={this.onKeyDown} onInput={this.onInput} onPaste={this.onPaste} - dangerouslySetInnerHTML={{ __html: this.props.contentToHtml(this.props.content) }} - /> + dangerouslySetInnerHTML={{ __html: _.escape(this.props.content) }} + ></div> ) } @@ -78,7 +83,6 @@ export default class EditorBase extends Component { onMouseDown(e) { this._mouseDown = true window.addEventListener('mouseup', this.onMouseUp) - this.props.onMouseDown(e) } onMouseUp() { @@ -94,7 +98,7 @@ export default class EditorBase extends Component { } onFocus(e) { - if (this._mouseDown || this._ignore_events || this.state.editable) { + if (this._mouseDown || this._ignore_events || this.state.editable || this.props.readonly) { return } @@ -114,31 +118,29 @@ export default class EditorBase extends Component { range = document.caretRangeFromPoint(e.clientX, e.clientY) } else { range = document.createRange() - range.selectNodeContents(ReactDOM.findDOMNode(this)) + range.selectNodeContents(this.input) } this._ignore_events = true this.setState({ editable: true }, () => { - const node = ReactDOM.findDOMNode(this) - node.blur() - node.focus() + this.input.blur() + this.input.focus() this._ignore_events = false + range.selectNodeContents(this.input) + sel.removeAllRanges(); + sel.addRange(range); }) } onBlur(e) { - if (this._ignore_events) { + if (this._ignore_events || this.props.readonly) { return } window.getSelection().removeAllRanges() //make sure that selection is cleared on blur this.setState({ editable: false }) - this.props.onDone(this.props.nodeToContent(ReactDOM.findDOMNode(this))) - this.props.onBlur(e) + this.props.onDone(this.input.textContent) } - reset() { - ReactDOM.findDOMNode(this).innerHTML = this.props.contentToHtml(this.props.content) - } onKeyDown(e) { e.stopPropagation() @@ -146,20 +148,21 @@ export default class EditorBase extends Component { case Key.ESC: e.preventDefault() this.reset() - this.stop() + this.blur() break case Key.ENTER: - if (this.props.submitOnEnter && !e.shiftKey) { + if (!e.shiftKey) { e.preventDefault() - this.stop() + this.blur() } break default: break } + this.props.onKeyDown(e) } onInput() { - this.props.onInput(this.props.nodeToContent(ReactDOM.findDOMNode(this))) + this.props.onInput(this.input.textContent) } } |