diff options
| author | Jason <jason.daurus@gmail.com> | 2016-07-04 12:59:11 -0400 | 
|---|---|---|
| committer | Jason <jason.daurus@gmail.com> | 2016-07-05 11:12:07 +0800 | 
| commit | 9bfde2a3ae7fada86911dc96f1ed35a21aae40c3 (patch) | |
| tree | cede8b5d9aab144e24775d0f6d2d926f5103bb23 | |
| parent | 8f73dc79c073a5fc955b5d13d9eb421f38868bf9 (diff) | |
| download | mitmproxy-9bfde2a3ae7fada86911dc96f1ed35a21aae40c3.tar.gz mitmproxy-9bfde2a3ae7fada86911dc96f1ed35a21aae40c3.tar.bz2 mitmproxy-9bfde2a3ae7fada86911dc96f1ed35a21aae40c3.zip  | |
[web] change ducks/ui
| -rw-r--r-- | web/src/js/components/FlowView.jsx | 53 | ||||
| -rw-r--r-- | web/src/js/components/Header.jsx | 3 | ||||
| -rw-r--r-- | web/src/js/components/Header/MainMenu.jsx | 17 | ||||
| -rw-r--r-- | web/src/js/components/MainView.jsx | 135 | ||||
| -rw-r--r-- | web/src/js/components/ProxyApp.jsx | 80 | ||||
| -rw-r--r-- | web/src/js/ducks/ui.js | 188 | ||||
| -rwxr-xr-x | web/src/js/ducks/views/main.js | 31 | 
7 files changed, 278 insertions, 229 deletions
diff --git a/web/src/js/components/FlowView.jsx b/web/src/js/components/FlowView.jsx index 0ef6e5cd..1a04c915 100644 --- a/web/src/js/components/FlowView.jsx +++ b/web/src/js/components/FlowView.jsx @@ -1,4 +1,5 @@  import React, { Component } from 'react' +import { connect } from 'react-redux'  import _ from 'lodash'  import Nav from './FlowView/Nav' @@ -6,47 +7,29 @@ import { Request, Response, ErrorView as Error } from './FlowView/Messages'  import Details from './FlowView/Details'  import Prompt from './Prompt' +import { setPrompt, selectTab } from '../ducks/ui' +  export default class FlowView extends Component {      static allTabs = { Request, Response, Error, Details }      constructor(props, context) {          super(props, context) - -        this.state = { prompt: false } - -        this.closePrompt = this.closePrompt.bind(this) -        this.selectTab = this.selectTab.bind(this) -    } - -    getTabs() { -        return ['request', 'response', 'error'].filter(k => this.props.flow[k]).concat(['details']) -    } - -    nextTab(increment) { -        const tabs = this.getTabs() -        // JS modulo operator doesn't correct negative numbers, make sure that we are positive. -        this.selectTab(tabs[(tabs.indexOf(this.props.tab) + increment + tabs.length) % tabs.length]) +        this.onPromptFinish = this.onPromptFinish.bind(this)      } -    selectTab(panel) { -        this.props.updateLocation(`/flows/${this.props.flow.id}/${panel}`) -    } - -    closePrompt(edit) { -        this.setState({ prompt: false }) +    onPromptFinish(edit) { +        this.props.setPrompt(false)          if (edit && this.tabComponent) {              this.tabComponent.edit(edit)          }      } -    promptEdit() { -        let options - +    getPromptOptions() {          switch (this.props.tab) {              case 'request': -                options = [ +                return [                      'method',                      'url',                      { text: 'http version', key: 'v' }, @@ -55,7 +38,7 @@ export default class FlowView extends Component {                  break              case 'response': -                options = [ +                return [                      { text: 'http version', key: 'v' },                      'code',                      'message', @@ -69,13 +52,11 @@ export default class FlowView extends Component {              default:                  throw 'Unknown tab for edit: ' + this.props.tab          } - -        this.setState({ prompt: { options, done: this.closePrompt } })      }      render() { -        const tabs = this.getTabs()          let { flow, tab: active, updateFlow } = this.props +        const tabs = ['request', 'response', 'error'].filter(k => flow[k]).concat(['details'])          if (tabs.indexOf(active) < 0) {              if (active === 'response' && flow.error) { @@ -95,13 +76,23 @@ export default class FlowView extends Component {                      flow={flow}                      tabs={tabs}                      active={active} -                    onSelectTab={this.selectTab} +                    onSelectTab={this.props.selectTab}                  />                  <Tab ref={ tab => this.tabComponent = tab } flow={flow} updateFlow={updateFlow} />                  {this.state.prompt && ( -                    <Prompt {...this.state.prompt}/> +                    <Prompt options={this.getPromptOptions()} done={this.onPromptFinish} />                  )}              </div>          )      }  } + +export default connect( +    state => ({ +        needEdit: state.ui.needEdit, +    }), +    { +        setPrompt, +        selectTab, +    } +)(FlowView) diff --git a/web/src/js/components/Header.jsx b/web/src/js/components/Header.jsx index b6ef1cc7..7f1fa69f 100644 --- a/web/src/js/components/Header.jsx +++ b/web/src/js/components/Header.jsx @@ -17,7 +17,7 @@ class Header extends Component {      }      render() { -        const { updateLocation, query, selectedFlow, activeMenu} = this.props +        const { query, selectedFlow, activeMenu} = this.props          let entries = [...Header.entries]          if(selectedFlow) @@ -41,7 +41,6 @@ class Header extends Component {                  <div className="menu">                      <Active                          ref="active" -                        updateLocation={updateLocation}                          query={query}                      />                  </div> diff --git a/web/src/js/components/Header/MainMenu.jsx b/web/src/js/components/Header/MainMenu.jsx index 48fea5a2..27a4be60 100644 --- a/web/src/js/components/Header/MainMenu.jsx +++ b/web/src/js/components/Header/MainMenu.jsx @@ -3,6 +3,7 @@ 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'  class MainMenu extends Component { @@ -12,8 +13,8 @@ class MainMenu extends Component {      static propTypes = {          query: PropTypes.object.isRequired,          settings: PropTypes.object.isRequired, -        updateLocation: PropTypes.func.isRequired,          updateSettings: PropTypes.func.isRequired, +        updateQuery: PropTypes.func.isRequired,      }      constructor(props, context) { @@ -22,12 +23,19 @@ class MainMenu extends Component {          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.updateLocation(undefined, { [Query.SEARCH]: val }) +        this.props.updateQuery({ [Query.SEARCH]: val })      }      onHighlightChange(val) { -        this.props.updateLocation(undefined, { [Query.HIGHLIGHT]: val }) +        this.props.updateQuery({ [Query.HIGHLIGHT]: val })      }      render() { @@ -70,9 +78,12 @@ class MainMenu extends Component {  export default connect(      state => ({          settings: state.settings.settings, +        selectedInput: state.ui.selectedInput      }),      {          updateSettings, +        updateQuery, +        setSelectedInput      },      null,      { diff --git a/web/src/js/components/MainView.jsx b/web/src/js/components/MainView.jsx index 93f7b299..7bb6f196 100644 --- a/web/src/js/components/MainView.jsx +++ b/web/src/js/components/MainView.jsx @@ -20,10 +20,6 @@ class MainView extends Component {       * @todo replace with mapStateToProps       */      componentWillReceiveProps(nextProps) { -        // Update redux store with route changes -        if (nextProps.routeParams.flowId !== (nextProps.selectedFlow || {}).id) { -            this.props.selectFlow(nextProps.routeParams.flowId) -        }          if (nextProps.location.query[Query.SEARCH] !== nextProps.filter) {              this.props.updateFilter(nextProps.location.query[Query.SEARCH], false)          } @@ -32,127 +28,6 @@ class MainView extends Component {          }      } -    /** -     * @todo move to actions -     */ -    selectFlow(flow) { -        if (flow) { -            this.props.updateLocation(`/flows/${flow.id}/${this.props.routeParams.detailTab || 'request'}`) -        } else { -            this.props.updateLocation('/flows') -        } -    } - -    /** -     * @todo move to actions -     */ -    selectFlowRelative(shift) { -        const { flows, routeParams, selectedFlow } = this.props -        let index = 0 -        if (!routeParams.flowId) { -            if (shift < 0) { -                index = flows.length - 1 -            } -        } else { -            index = Math.min( -                Math.max(0, flows.indexOf(selectedFlow) + shift), -                flows.length - 1 -            ) -        } -        this.selectFlow(flows[index]) -    } - -    /** -     * @todo move to actions -     */ -    onMainKeyDown(e) { -        var flow = this.props.selectedFlow -        if (e.ctrlKey) { -            return -        } -        switch (e.keyCode) { -            case Key.K: -            case Key.UP: -                this.selectFlowRelative(-1) -                break -            case Key.J: -            case Key.DOWN: -                this.selectFlowRelative(+1) -                break -            case Key.SPACE: -            case Key.PAGE_DOWN: -                this.selectFlowRelative(+10) -                break -            case Key.PAGE_UP: -                this.selectFlowRelative(-10) -                break -            case Key.END: -                this.selectFlowRelative(+1e10) -                break -            case Key.HOME: -                this.selectFlowRelative(-1e10) -                break -            case Key.ESC: -                this.selectFlow(null) -                break -            case Key.H: -            case Key.LEFT: -                if (this.refs.flowDetails) { -                    this.refs.flowDetails.nextTab(-1) -                } -                break -            case Key.L: -            case Key.TAB: -            case Key.RIGHT: -                if (this.refs.flowDetails) { -                    this.refs.flowDetails.nextTab(+1) -                } -                break -            case Key.C: -                if (e.shiftKey) { -                    this.props.clearFlows() -                } -                break -            case Key.D: -                if (flow) { -                    if (e.shiftKey) { -                        this.props.duplicateFlow(flow) -                    } else { -                        this.props.removeFlow(flow) -                    } -                } -                break -            case Key.A: -                if (e.shiftKey) { -                    this.props.acceptAllFlows() -                } else if (flow && flow.intercepted) { -                    this.props.acceptFlow(flow) -                } -                break -            case Key.R: -                if (!e.shiftKey && flow) { -                    this.props.replayFlow(flow) -                } -                break -            case Key.V: -                if (e.shiftKey && flow && flow.modified) { -                    this.props.revertFlow(flow) -                } -                break -            case Key.E: -                if (this.refs.flowDetails) { -                    this.refs.flowDetails.promptEdit() -                } -                break -            case Key.SHIFT: -                break -            default: -                console.debug('keydown', e.keyCode) -                return -        } -        e.preventDefault() -    } -      render() {          const { flows, selectedFlow, highlight } = this.props          return ( @@ -162,7 +37,7 @@ class MainView extends Component {                      flows={flows}                      selected={selectedFlow}                      highlight={highlight} -                    onSelect={flow => this.selectFlow(flow)} +                    onSelect={flow => this.props.selectFlow(flow.id)}                  />                  {selectedFlow && [                      <Splitter key="splitter"/>, @@ -171,7 +46,6 @@ class MainView extends Component {                          ref="flowDetails"                          tab={this.props.routeParams.detailTab}                          query={this.props.query} -                        updateLocation={this.props.updateLocation}                          updateFlow={data => this.props.updateFlow(selectedFlow, data)}                          flow={selectedFlow}                      /> @@ -193,13 +67,6 @@ export default connect(          updateFilter,          updateHighlight,          updateFlow: flowsActions.update, -        clearFlows: flowsActions.clear, -        duplicateFlow: flowsActions.duplicate, -        removeFlow: flowsActions.remove, -        acceptAllFlows: flowsActions.acceptAll, -        acceptFlow: flowsActions.accept, -        replayFlow: flowsActions.replay, -        revertFlow: flowsActions.revert,      },      undefined,      { withRef: true } diff --git a/web/src/js/components/ProxyApp.jsx b/web/src/js/components/ProxyApp.jsx index 1ac979bc..e8c0e6d6 100644 --- a/web/src/js/components/ProxyApp.jsx +++ b/web/src/js/components/ProxyApp.jsx @@ -4,6 +4,7 @@ import _ from 'lodash'  import { connect } from 'react-redux'  import { init as appInit, destruct as appDestruct } from '../ducks/app' +import { onKeyDown } from '../ducks/ui'  import Header from './Header'  import EventLog from './EventLog'  import Footer from './Footer' @@ -24,13 +25,23 @@ class ProxyAppMain extends Component {          this.focus = this.focus.bind(this)          this.onKeyDown = this.onKeyDown.bind(this) -        this.updateLocation = this.updateLocation.bind(this)      }      componentWillMount() {          this.props.appInit()      } +    componentWillReceiveProps(nextProps) { +        if(nextProps.query === this.props.query && nextProps.flowId === this.props.flowId && nextProps.panel === this.props.panel) { +            return +        } +        if(nextProps.flowId) { +            this.context.router.replace({ pathname: `/flows/${nextProps.flowId}/${nextProps.panel}`, query: nextProps.query }) +        } else { +            this.context.router.replace({ pathname: '/flows', query: nextProps.query }) +        } +    } +      /**       * @todo listen to window's key events       */ @@ -63,72 +74,21 @@ class ProxyAppMain extends Component {       * @todo bind on window       */      onKeyDown(e) { -        let name = null - -        switch (e.keyCode) { -            case Key.I: -                name = 'intercept' -                break -            case Key.L: -                name = 'search' -                break -            case Key.H: -                name = 'highlight' -                break -            default: -                let main = this.refs.view -                if (this.refs.view.refs.wrappedInstance) { -                    main = this.refs.view.refs.wrappedInstance -                } -                if (main.onMainKeyDown) { -                    main.onMainKeyDown(e) -                } -                return // don't prevent default then +        if (e.ctrlKey) { +            return          } - -        if (name) { -            const headerComponent = this.refs.header.refs.wrappedInstance || this.refs.header -            headerComponent.setState({ active: Header.entries[0] }, () => { -                const active = headerComponent.refs.active.refs.wrappedInstance || headerComponent.refs.active -                active.refs[name].select() -            }) -        } - +        this.props.onKeyDown(e.keyCode)          e.preventDefault()      } -    /** -     * @todo move to actions -     */ -    updateLocation(pathname, queryUpdate) { -        if (pathname === undefined) { -            pathname = this.props.location.pathname -        } -        const query = this.props.location.query -        for (const key of Object.keys(queryUpdate || {})) { -            query[key] = queryUpdate[key] || undefined -        } -        this.context.router.replace({ pathname, query }) -    } - -    /** -     * @todo pass in with props -     */ -    getQuery() { -        // For whatever reason, react-router always returns the same object, which makes comparing -        // the current props with nextProps impossible. As a workaround, we just clone the query object. -        return _.clone(this.props.location.query) -    } -      render() { -        const { showEventLog, location, children } = this.props -        const query = this.getQuery() +        const { showEventLog, location, children, query } = this.props          return (              <div id="container" tabIndex="0" onKeyDown={this.onKeyDown}> -                <Header ref="header" updateLocation={this.updateLocation} query={query} /> +                <Header ref="header" query={query} />                  {React.cloneElement(                      children, -                    { ref: 'view', location, query, updateLocation: this.updateLocation } +                    { ref: 'view', location, query }                  )}                  {showEventLog && (                      <EventLog key="eventlog"/> @@ -143,9 +103,13 @@ export default connect(      state => ({          showEventLog: state.eventLog.visible,          settings: state.settings.settings, +        query: state.ui.query, +        panel: state.ui.panel, +        flowId: state.flows.views.main.selected[0]      }),      {          appInit,          appDestruct, +        onKeyDown      }  )(ProxyAppMain) diff --git a/web/src/js/ducks/ui.js b/web/src/js/ducks/ui.js index f513f49c..bc64ffa8 100644 --- a/web/src/js/ducks/ui.js +++ b/web/src/js/ducks/ui.js @@ -1,11 +1,22 @@ -import { SELECT as SELECT_FLOW } from './views/main' +import { SELECT as SELECT_FLOW, selectRelative as selectFlowRelative } from './views/main' +import { Key } from '../utils.js' +import * as flowsActions from '../ducks/flows'  export const SET_ACTIVE_MENU = 'UI_SET_ACTIVE_MENU'  export const SET_CONTENT_VIEW = 'UI_SET_CONTENT_VIEW' +export const SET_SELECTED_INPUT = 'SET_SELECTED_INPUT' +export const UPDATE_QUERY = 'UPDATE_QUERY' +export const SELECT_TAB = 'SELECT_TAB' +export const SELECT_TAB_RELATIVE = 'SELECT_TAB_RELATIVE' +export const SET_PROMPT = 'SET_PROMPT'  const defaultState = {      activeMenu: 'Start', +    selectedInput: null, +    promptOpen: false,      contentView: 'ViewAuto', +    query: {}, +    panel: 'request'  }  export default function reducer(state = defaultState, action) { @@ -40,6 +51,43 @@ export default function reducer(state = defaultState, action) {                  contentView: action.contentView,              } +        case SET_SELECTED_INPUT: +            return { +                ...state, +                selectedInput: action.input +            } + +        case UPDATE_QUERY: +            return { +                ...state, +                query: { ...state.query, ...action.query } +            } + +        case SELECT_TAB: +            return { +                ...state, +                panel: action.panel +            } + +        case SELECT_TAB_RELATIVE: +            if (!action.flow || action.shift === null) { +                return { +                    ...state, +                    panel: 'request' +                } +            } +            const tabs = ['request', 'response', 'error'].filter(k => action.flow[k]).concat(['details']) +            return { +                ...state, +                panel: tabs[(tabs.indexOf(state.panel) + action.shift + tabs.length) % tabs.length] +            } + +        case SET_PROMPT: +            return { +                ...state, +                promptOpen: action.open, +            } +          default:              return state      } @@ -52,3 +100,141 @@ export function setActiveMenu(activeMenu) {  export function setContentView(contentView) {      return { type: SET_CONTENT_VIEW, contentView }  } + +export function setSelectedInput(input) { +    return { type: SET_SELECTED_INPUT, input } +} + +export function updateQuery(query) { +    return { type: UPDATE_QUERY, query } +} + +export function selectTab(panel) { +    return { type: SELECT_TAB, panel } +} + +export function selectTabRelative(shift) { +    return (dispatch, getState) => { +        let flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +        dispatch({ type: SELECT_TAB_RELATIVE, shift, flow }) +    } +} + +export function setPrompt(open) { +    return { type: SET_PROMPT, open } +} + +export function onKeyDown(key, shiftKey) { +    return (dispatch, getState) => { +        switch (key) { + +            case Key.I: +                dispatch(setSelectedInput('intercept')) +                break + +            case Key.L: +                dispatch(setSelectedInput('search')) +                break + +            case Key.H: +                dispatch(setSelectedInput('highlight')) +                break + +            case Key.K: +            case Key.UP: +                dispatch(selectFlowRelative(-1)) +                break + +            case Key.J: +            case Key.DOWN: +                dispatch(selectFlowRelative(+1)) +                break + +            case Key.SPACE: +            case Key.PAGE_DOWN: +                dispatch(selectFlowRelative(+10)) +                break + +            case Key.PAGE_UP: +                dispatch(selectFlowRelative(-10)) +                break + +            case Key.END: +                dispatch(selectFlowRelative(+1e10)) +                break + +            case Key.HOME: +                dispatch(selectFlowRelative(-1e10)) +                break + +            case Key.ESC: +                dispatch(selectFlowRelative(null)) +                dispatch(selectTabRelative(null)) +                break + +            case Key.H: +            case Key.LEFT: +                dispatch(selectTabRelative(-1)) +                break + +            case Key.L: +            case Key.TAB: +            case Key.RIGHT: +                dispatch(selectTabRelative(+1)) +                break + +            case Key.C: +                if (shiftKey) { +                    dispatch(flowsActions.clear()) +                } +                break + +            case Key.D: { +                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +                if (!flow) { +                    return +                } +                if (shiftKey) { +                    dispatch(flowsActions.duplicate(flow)) +                } else { +                    dispatch(flowsActions.remove(flow)) +                } +                break +            } + +            case Key.A: { +                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +                if (shiftKey) { +                    dispatch(flowsActions.acceptAll()) +                } else if (flow && flow.intercepted) { +                    dispatch(flowsActions.accept(flow)) +                } +                break +            } + +            case Key.R: { +                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +                if (!shiftKey && flow) { +                    dispatch(flowsActions.replay(flow)) +                } +                break +            } + +            case Key.V: { +                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +                if (!shiftKey && flow && flow.modified) { +                    dispatch(flowsActions.revert(flow)) +                } +                break +            } + +            case Key.E: +                dispatch(setPrompt(true)) +                break + +            default: +                return () => {} +        } +        event.preventDefault() +    } +} diff --git a/web/src/js/ducks/views/main.js b/web/src/js/ducks/views/main.js index f4968de4..db9de619 100755 --- a/web/src/js/ducks/views/main.js +++ b/web/src/js/ducks/views/main.js @@ -7,6 +7,7 @@ export const UPDATE_FILTER = 'FLOW_VIEWS_MAIN_UPDATE_FILTER'  export const UPDATE_SORT = 'FLOW_VIEWS_MAIN_UPDATE_SORT'  export const UPDATE_HIGHLIGHT = 'FLOW_VIEWS_MAIN_UPDATE_HIGHLIGHT'  export const SELECT = 'FLOW_VIEWS_MAIN_SELECT' +export const SELECT_RELATIVE = 'SELECT_RELATIVE'  const sortKeyFuns = { @@ -52,6 +53,27 @@ export default function reduce(state = defaultState, action) {                  selected: [action.id]              } +        case SELECT_RELATIVE: +            if(action.shift === null) { +                return { +                    ...state, +                    selected: [] +                } +            } +            let id = state.selected[0] +            let index = 0 +            if(!id && action.shift < 0) { +                index = state.view.data.length - 1 +            } else if(id) { +                index = state.view.indexOf[id] + action.shift +                index = index < 0 ? 0 : index +                index = index > state.view.data.length - 1 ? state.view.data.length - 1 : index +            } +            return { +                ...state, +                selected: [state.view.data[index].id] +            } +          case UPDATE_FILTER:              return {                  ...state, @@ -171,6 +193,15 @@ export function select(id) {  }  /** + * @public + */ +export function selectRelative(shift) { +    return (dispatch, getState) => { +        dispatch({ type: SELECT_RELATIVE, currentSelection: getState().flows.views.main.selected[0], shift }) +    } +} + +/**   * @private   */  function makeFilter(filter) {  | 
