[web] fix actions

This commit is contained in:
Jason 2016-06-25 02:18:52 +08:00
parent 8da623c603
commit 2b7923b4f4
14 changed files with 147 additions and 103 deletions

File diff suppressed because one or more lines are too long

View File

@ -75,7 +75,7 @@ export default class FlowView extends Component {
render() {
const tabs = this.getTabs()
let { flow, tab: active } = this.props
let { flow, tab: active, onUpdate } = this.props
if (tabs.indexOf(active) < 0) {
if (active === 'response' && flow.error) {
@ -97,7 +97,7 @@ export default class FlowView extends Component {
active={active}
onSelectTab={this.selectTab}
/>
<Tab ref="tab" flow={flow}/>
<Tab ref="tab" flow={flow} onUpdate={onUpdate} />
{this.state.prompt && (
<Prompt {...this.state.prompt}/>
)}

View File

@ -1,7 +1,6 @@
import React, { Component } from 'react'
import React, { Component, PropTypes } from 'react'
import _ from 'lodash'
import { FlowActions } from '../../actions.js'
import { RequestUtils, isValidHttpVersion, parseUrl, parseHttpVersion } from '../../flow/utils.js'
import { Key, formatTimeStamp } from '../../utils.js'
import ContentView from '../ContentView'
@ -11,21 +10,21 @@ import Headers from './Headers'
class RequestLine extends Component {
render() {
const { flow } = this.props
const { flow, onUpdate } = this.props
return (
<div className="first-line request-line">
<ValueEditor
ref="method"
content={flow.request.method}
onDone={method => FlowActions.update(flow, { request: { method } })}
onDone={method => onUpdate({ request: { method } })}
inline
/>
&nbsp;
<ValueEditor
ref="url"
content={RequestUtils.pretty_url(flow.request)}
onDone={url => FlowActions.update(flow, { request: Object.assign({ path: '' }, parseUrl(url)) })}
onDone={url => onUpdate({ request: Object.assign({ path: '' }, parseUrl(url)) })}
isValid={url => !!parseUrl(url).host}
inline
/>
@ -33,7 +32,7 @@ class RequestLine extends Component {
<ValueEditor
ref="httpVersion"
content={flow.request.http_version}
onDone={ver => FlowActions.update(flow, { request: { http_version: parseHttpVersion(ver) } })}
onDone={ver => onUpdate({ request: { http_version: parseHttpVersion(ver) } })}
isValid={isValidHttpVersion}
inline
/>
@ -45,14 +44,14 @@ class RequestLine extends Component {
class ResponseLine extends Component {
render() {
const { flow } = this.props
const { flow, onUpdate } = this.props
return (
<div className="first-line response-line">
<ValueEditor
ref="httpVersion"
content={flow.response.http_version}
onDone={nextVer => FlowActions.update(flow, { response: { http_version: parseHttpVersion(nextVer) } })}
onDone={nextVer => onUpdate({ response: { http_version: parseHttpVersion(nextVer) } })}
isValid={isValidHttpVersion}
inline
/>
@ -60,7 +59,7 @@ class ResponseLine extends Component {
<ValueEditor
ref="code"
content={flow.response.status_code + ''}
onDone={code => FlowActions.update(flow, { response: { code: parseInt(code) } })}
onDone={code => onUpdate({ response: { code: parseInt(code) } })}
isValid={code => /^\d+$/.test(code)}
inline
/>
@ -68,7 +67,7 @@ class ResponseLine extends Component {
<ValueEditor
ref="msg"
content={flow.response.reason}
onDone={msg => FlowActions.update(flow, { response: { msg } })}
onDone={msg => onUpdate({ response: { msg } })}
inline
/>
</div>
@ -79,15 +78,15 @@ class ResponseLine extends Component {
export class Request extends Component {
render() {
const { flow } = this.props
const { flow, onUpdate } = this.props
return (
<section className="request">
<RequestLine ref="requestLine" flow={flow}/>
<RequestLine ref="requestLine" flow={flow} onUpdate={onUpdate} />
<Headers
ref="headers"
message={flow.request}
onChange={headers => FlowActions.update(flow, { request: { headers } })}
onChange={headers => onUpdate({ request: { headers } })}
/>
<hr/>
<ContentView flow={flow} message={flow.request}/>
@ -118,15 +117,15 @@ export class Request extends Component {
export class Response extends Component {
render() {
const { flow } = this.props
const { flow, onUpdate } = this.props
return (
<section className="response">
<ResponseLine ref="responseLine" flow={flow}/>
<ResponseLine ref="responseLine" flow={flow} onUpdate={onUpdate} />
<Headers
ref="headers"
message={flow.response}
onChange={headers => FlowActions.update(flow, { response: { headers } })}
onChange={headers => onUpdate({ response: { headers } })}
/>
<hr/>
<ContentView flow={flow} message={flow.response}/>
@ -153,16 +152,3 @@ export class Response extends Component {
}
}
}
export function Error({ flow }) {
return (
<section>
<div className="alert alert-warning">
{flow.error.msg}
<div>
<small>{formatTimeStamp(flow.error.timestamp)}</small>
</div>
</div>
</section>
)
}

View File

@ -1,6 +1,7 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import classnames from 'classnames'
import { FlowActions } from '../../actions.js'
import * as flowsActions from '../../ducks/flows'
NavAction.propTypes = {
icon: PropTypes.string.isRequired,
@ -27,9 +28,14 @@ Nav.propTypes = {
active: PropTypes.string.isRequired,
tabs: PropTypes.array.isRequired,
onSelectTab: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
onDuplicate: PropTypes.func.isRequired,
onReplay: PropTypes.func.isRequired,
onAccept: PropTypes.func.isRequired,
onRevert: PropTypes.func.isRequired,
}
export default function Nav({ flow, active, tabs, onSelectTab }) {
function Nav({ flow, active, tabs, onSelectTab, onRemove, onDuplicate, onReplay, onAccept, onRevert }) {
return (
<nav className="nav-tabs nav-tabs-sm">
{tabs.map(tab => (
@ -43,15 +49,26 @@ export default function Nav({ flow, active, tabs, onSelectTab }) {
{_.capitalize(tab)}
</a>
))}
<NavAction title="[d]elete flow" icon="fa-trash" onClick={() => FlowActions.delete(flow)} />
<NavAction title="[D]uplicate flow" icon="fa-copy" onClick={() => FlowActions.duplicate(flow)} />
<NavAction disabled title="[r]eplay flow" icon="fa-repeat" onClick={() => FlowActions.replay(flow)} />
<NavAction title="[d]elete flow" icon="fa-trash" onClick={() => onRemove(flow)} />
<NavAction title="[D]uplicate flow" icon="fa-copy" onClick={() => onDuplicate(flow)} />
<NavAction disabled title="[r]eplay flow" icon="fa-repeat" onClick={() => onReplay(flow)} />
{flow.intercepted && (
<NavAction title="[a]ccept intercepted flow" icon="fa-play" onClick={() => FlowActions.accept(flow)} />
<NavAction title="[a]ccept intercepted flow" icon="fa-play" onClick={() => onAccept(flow)} />
)}
{flow.modified && (
<NavAction title="revert changes to flow [V]" icon="fa-history" onClick={() => FlowActions.revert(flow)} />
<NavAction title="revert changes to flow [V]" icon="fa-history" onClick={() => onRevert(flow)} />
)}
</nav>
)
}
export default connect(
null,
{
onRemove: flowsActions.remove,
onDuplicate: flowsActions.duplicate,
onReplay: flowsActions.replay,
onAccept: flowsActions.accept,
onRevert: flowsActions.revert,
}
)(Nav)

View File

@ -14,8 +14,6 @@ class Header extends Component {
handleClick(active, e) {
e.preventDefault()
this.props.setActiveMenu(active.title)
// this.props.updateLocation(active.route)
// this.setState({ active })
}
render() {
@ -42,6 +40,7 @@ class Header extends Component {
</nav>
<div className="menu">
<Active
ref="active"
updateLocation={updateLocation}
query={query}
/>
@ -50,6 +49,7 @@ class Header extends Component {
)
}
}
export default connect(
state => ({
selectedFlow: state.flows.views.main.selected[0],
@ -57,5 +57,9 @@ export default connect(
}),
{
setActiveMenu,
},
null,
{
withRef: true,
}
)(Header)

View File

@ -1,8 +1,9 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import classnames from 'classnames'
import * as flowActions from '../../ducks/flows'
import * as flowsActions from '../../ducks/flows'
export default class FileMenu extends Component {
class FileMenu extends Component {
constructor(props, context) {
super(props, context)
@ -35,7 +36,7 @@ export default class FileMenu extends Component {
onNewClick(e) {
e.preventDefault()
if (confirm('Delete all flows?')) {
flowActions.clear()
this.props.onClear()
}
}
@ -47,14 +48,14 @@ export default class FileMenu extends Component {
onOpenFile(e) {
e.preventDefault()
if (e.target.files.length > 0) {
flowActions.upload(e.target.files[0])
this.props.onUpload(e.target.files[0])
this.fileInput.value = ''
}
}
onSaveClick(e) {
e.preventDefault()
flowActions.download()
this.props.onDownload()
}
render() {
@ -98,3 +99,12 @@ export default class FileMenu extends Component {
)
}
}
export default connect(
null,
{
onClear: flowsActions.clear,
onUpload: flowsActions.upload,
onDownload: flowsActions.download,
}
)(FileMenu)

View File

@ -1,8 +1,8 @@
import React, { PropTypes } from 'react'
import Button from '../common/Button'
import { FlowActions } from '../../actions.js'
import { MessageUtils } from '../../flow/utils.js'
import { connect } from 'react-redux'
import Button from '../common/Button'
import { MessageUtils } from '../../flow/utils.js'
import * as flowsActions from '../../ducks/flows'
FlowMenu.title = 'Flow'
@ -10,16 +10,16 @@ FlowMenu.propTypes = {
flow: PropTypes.object.isRequired,
}
function FlowMenu({ flow }) {
function FlowMenu({ flow, onAccept, onReplay, onDuplicate, onRemove, onRevert }) {
return (
<div>
<div className="menu-row">
<Button disabled={!flow.intercepted} title="[a]ccept intercepted flow" text="Accept" icon="fa-play" onClick={() => FlowActions.accept(flow)} />
<Button title="[r]eplay flow" text="Replay" icon="fa-repeat" onClick={FlowActions.replay.bind(null, flow)} />
<Button title="[D]uplicate flow" text="Duplicate" icon="fa-copy" onClick={FlowActions.duplicate.bind(null, flow)} />
<Button title="[d]elete flow" text="Delete" icon="fa-trash" onClick={FlowActions.delete.bind(null, flow)}/>
<Button disabled={!flow.modified} title="revert changes to flow [V]" text="Revert" icon="fa-history" onClick={() => FlowActions.revert(flow)} />
<Button disabled={!flow.intercepted} title="[a]ccept intercepted flow" text="Accept" icon="fa-play" onClick={() => onAccept(flow)} />
<Button title="[r]eplay flow" text="Replay" icon="fa-repeat" onClick={() => onReplay(flow)} />
<Button title="[D]uplicate flow" text="Duplicate" icon="fa-copy" onClick={() => onDuplicate(flow)} />
<Button title="[d]elete flow" text="Delete" icon="fa-trash" onClick={() => onRemove(flow)}/>
<Button disabled={!flow.modified} title="revert changes to flow [V]" text="Revert" icon="fa-history" onClick={() => onRevert(flow)} />
<Button title="download" text="Download" icon="fa-download" onClick={() => window.location = MessageUtils.getContentURL(flow, flow.response)}/>
</div>
<div className="clearfix"/>
@ -30,5 +30,12 @@ function FlowMenu({ flow }) {
export default connect(
state => ({
flow: state.flows.list.byId[state.flows.views.main.selected[0]],
})
}),
{
onAccept: flowsActions.accept,
onReplay: flowsActions.replay,
onDuplicate: flowsActions.duplicate,
onRemove: flowsActions.remove,
onRevert: flowsActions.revert,
}
)(FlowMenu)

View File

@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import FilterInput from './FilterInput'
import { Query } from '../../actions.js'
import { updateSettings } from '../../ducks/settings'
import { update as updateSettings } from '../../ducks/settings'
class MainMenu extends Component {
@ -73,5 +73,9 @@ export default connect(
}),
{
onSettingsChange: updateSettings,
},
null,
{
withRef: true,
}
)(MainMenu);

View File

@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import ToggleButton from '../common/ToggleButton'
import ToggleInputButton from '../common/ToggleInputButton'
import { updateSettings } from '../../ducks/settings'
import { update as updateSettings } from '../../ducks/settings'
OptionMenu.title = 'Options'

View File

@ -1,11 +1,11 @@
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { FlowActions } from '../actions.js'
import { Query } from '../actions.js'
import { Key } from '../utils.js'
import Splitter from './common/Splitter'
import FlowTable from './FlowTable'
import FlowView from './FlowView'
import * as flowsActions from '../ducks/flows'
import { select as selectFlow, updateFilter, updateHighlight } from '../ducks/views/main'
class MainView extends Component {
@ -110,33 +110,33 @@ class MainView extends Component {
break
case Key.C:
if (e.shiftKey) {
FlowActions.clear()
this.props.onClear()
}
break
case Key.D:
if (flow) {
if (e.shiftKey) {
FlowActions.duplicate(flow)
this.props.onDuplicate(flow)
} else {
FlowActions.delete(flow)
this.props.onRemove(flow)
}
}
break
case Key.A:
if (e.shiftKey) {
FlowActions.accept_all()
this.props.onAcceptAll()
} else if (flow && flow.intercepted) {
FlowActions.accept(flow)
this.props.onAccept(flow)
}
break
case Key.R:
if (!e.shiftKey && flow) {
FlowActions.replay(flow)
this.props.onReplay(flow)
}
break
case Key.V:
if (e.shiftKey && flow && flow.modified) {
FlowActions.revert(flow)
this.props.onRevert(flow)
}
break
case Key.E:
@ -147,7 +147,6 @@ class MainView extends Component {
case Key.SHIFT:
break
default:
console.debug('keydown', e.keyCode)
return
}
e.preventDefault()
@ -172,6 +171,7 @@ class MainView extends Component {
tab={this.props.routeParams.detailTab}
query={this.props.query}
updateLocation={this.props.updateLocation}
onUpdate={attrs => this.props.onUpdate(selectedFlow, attrs)}
flow={selectedFlow}
/>
]}
@ -191,6 +191,14 @@ export default connect(
selectFlow,
updateFilter,
updateHighlight,
onUpdate: flowsActions.update,
onClear: flowsActions.clear,
onDuplicate: flowsActions.duplicate,
onRemove: flowsActions.remove,
onAcceptAll: flowsActions.acceptAll,
onAccept: flowsActions.accept,
onReplay: flowsActions.replay,
onRevert: flowsActions.revert,
},
undefined,
{ withRef: true }

View File

@ -37,7 +37,7 @@ export default function Prompt({ prompt, done, options }, context) {
}
return (
<div tabIndex="0" onKeyDown={onKeyDown} onClick={onClick} className="prompt-dialog">
<div tabIndex="0" onKeyDown={onKeyDown} className="prompt-dialog">
<div className="prompt-content">
{prompt || <strong>Select: </strong> }
{opts.map(opt => {
@ -49,7 +49,7 @@ export default function Prompt({ prompt, done, options }, context) {
return (
<span key={opt.key} className="option" onClick={onClick}>
{idx !== -1 ? opt.text.substring(0, idx) : opt.text + '('}
{prefix}<strong className="text-primary">{opt.key}</strong>
<strong className="text-primary">{opt.key}</strong>
{idx !== -1 ? opt.text.substring(idx + 1) : ')'}
</span>
)
@ -65,7 +65,7 @@ export default function Prompt({ prompt, done, options }, context) {
if (!key && event.keyCode !== Key.ESC && event.keyCode !== Key.ENTER) {
return
}
done(k || false)
done(key.key || false)
context.returnFocus()
}
}

View File

@ -77,8 +77,8 @@ class ProxyAppMain extends Component {
break
default:
let main = this.refs.view
if (this.refs.view.getWrappedInstance) {
main = this.refs.view.getWrappedInstance()
if (this.refs.view.refs.wrappedInstance) {
main = this.refs.view.refs.wrappedInstance
}
if (main.onMainKeyDown) {
main.onMainKeyDown(e)
@ -87,9 +87,10 @@ class ProxyAppMain extends Component {
}
if (name) {
const headerComponent = this.refs.header
const headerComponent = this.refs.header.refs.wrappedInstance || this.refs.header
headerComponent.setState({ active: Header.entries[0] }, () => {
headerComponent.refs.active.refs[name].select()
const active = headerComponent.refs.active.refs.wrappedInstance || headerComponent.refs.active
active.refs[name].select()
})
}

View File

@ -40,8 +40,8 @@ export default function reduce(state = defaultState, action) {
case REMOVE:
return {
...state,
list: reduceList(state.list, listActions.remove(action.item.id)),
views: reduceViews(state.views, viewsActions.remove(action.item.id)),
list: reduceList(state.list, listActions.remove(action.id)),
views: reduceViews(state.views, viewsActions.remove(action.id)),
}
case RECEIVE:
@ -113,7 +113,7 @@ export function revert(flow) {
* @public
*/
export function update(flow, body) {
fetchApi(`/flows/${flow.id}`, { method: 'PUT', body })
fetchApi.put(`/flows/${flow.id}`, body)
return { type: REQUEST_ACTION }
}
@ -152,13 +152,13 @@ export function handleWsMsg(msg) {
switch (msg.cmd) {
case websocketActions.CMD_ADD:
return add(msg.data)
return addItem(msg.data)
case websocketActions.CMD_UPDATE:
return update(msg.data.id, msg.data)
return updateItem(msg.data.id, msg.data)
case websocketActions.CMD_REMOVE:
return remove(msg.data.id)
return removeItem(msg.data.id)
case websocketActions.CMD_RESET:
return fetchData()
@ -185,20 +185,20 @@ export function receiveData(list) {
/**
* @private
*/
export function add(item) {
export function addItem(item) {
return { type: ADD, item }
}
/**
* @private
*/
export function update(id, item) {
export function updateItem(id, item) {
return { type: UPDATE, id, item }
}
/**
* @private
*/
export function remove(id) {
export function removeItem(id) {
return { type: REMOVE, id }
}

View File

@ -40,7 +40,7 @@ export function handleWsMsg(msg) {
switch (msg.cmd) {
case websocketActions.CMD_UPDATE:
return { type: UPDATE, settings: msg.data }
return updateSettings(msg.data)
default:
console.error('unknown settings update', msg)
@ -51,7 +51,7 @@ export function handleWsMsg(msg) {
/**
* @public
*/
export function updateSettings(settings) {
export function update(settings) {
fetchApi.put('/settings', settings)
return { type: REQUEST_UPDATE }
}
@ -69,3 +69,10 @@ export function fetchData() {
export function receiveData(settings) {
return { type: RECEIVE, settings }
}
/**
* @private
*/
export function updateSettings(settings) {
return { type: UPDATE, settings }
}