diff options
Diffstat (limited to 'web/src/js')
23 files changed, 317 insertions, 401 deletions
diff --git a/web/src/js/__tests__/ducks/flowView.js b/web/src/js/__tests__/ducks/flowView.js new file mode 100644 index 00000000..d5d9a6d9 --- /dev/null +++ b/web/src/js/__tests__/ducks/flowView.js @@ -0,0 +1,67 @@ +jest.unmock('../../ducks/flows') +jest.unmock('../../ducks/flowView') +jest.unmock('../../ducks/utils/view') +jest.unmock('../../ducks/utils/list') +jest.unmock('./tutils') + +import { createStore } from './tutils' + +import flows, * as flowActions from '../../ducks/flows' +import flowView, * as flowViewActions from '../../ducks/flowView' + + +function testStore() { +    let store = createStore({ +        flows, +        flowView +    }) +    for (let i of [1, 2, 3, 4]) { +        store.dispatch( +            flowActions.addFlow({ id: i }) +        ) +    } +    return store +} + +describe('select relative', () => { + +    function testSelect(start, relative, result) { +        const store = testStore() +        store.dispatch(flowActions.select(start)) +        expect(store.getState().flows.selected).toEqual(start ? [start] : []) +        store.dispatch(flowViewActions.selectRelative(relative)) +        expect(store.getState().flows.selected).toEqual([result]) +    } + +    describe('previous', () => { + +        it('should select the previous flow', () => { +            testSelect(3, -1, 2) +        }) + +        it('should not changed when first flow is selected', () => { +            testSelect(1, -1, 1) +        }) + +        it('should select first flow if no flow is selected', () => { +            testSelect(undefined, -1, 1) +        }) + +    }) + +    describe('next', () => { + +        it('should select the next flow', () => { +            testSelect(2, 1, 3) +        }) + +        it('should not changed when last flow is selected', () => { +            testSelect(4, 1, 4) +        }) + +        it('should select last flow if no flow is selected', () => { +            testSelect(undefined, 1, 4) +        }) + +    }) +}) diff --git a/web/src/js/__tests__/ducks/flows.js b/web/src/js/__tests__/ducks/flows.js new file mode 100644 index 00000000..2b261cb1 --- /dev/null +++ b/web/src/js/__tests__/ducks/flows.js @@ -0,0 +1,30 @@ +jest.unmock('../../ducks/flows'); + +import reduceFlows, * as flowActions from '../../ducks/flows' + + +describe('select flow', () => { + +    let state = reduceFlows(undefined, {}) +    for (let i of [1, 2, 3, 4]) { +        state = reduceFlows(state, flowActions.addFlow({ id: i })) +    } + +    it('should be possible to select a single flow', () => { +        expect(reduceFlows(state, flowActions.select(2))).toEqual( +            { +                ...state, +                selected: [2], +            } +        ) +    }) + +    it('should be possible to deselect a flow', () => { +        expect(reduceFlows({ ...state, selected: [1] }, flowActions.select())).toEqual( +            { +                ...state, +                selected: [], +            } +        ) +    }) +}) diff --git a/web/src/js/__tests__/ducks/tutils.js b/web/src/js/__tests__/ducks/tutils.js new file mode 100644 index 00000000..90a21b78 --- /dev/null +++ b/web/src/js/__tests__/ducks/tutils.js @@ -0,0 +1,12 @@ +jest.unmock('redux') +jest.unmock('redux-thunk') + +import { combineReducers, applyMiddleware, createStore as createReduxStore } from 'redux' +import thunk from 'redux-thunk' + +export function createStore(parts) { +    return createReduxStore( +        combineReducers(parts), +        applyMiddleware(...[thunk]) +    ) +} diff --git a/web/src/js/__tests__/ducks/ui.js b/web/src/js/__tests__/ducks/ui.js index ae2b75b9..d3242815 100644 --- a/web/src/js/__tests__/ducks/ui.js +++ b/web/src/js/__tests__/ducks/ui.js @@ -1,14 +1,8 @@ -jest.unmock('lodash') -jest.unmock('redux') -jest.unmock('redux-thunk')  jest.unmock('../../ducks/ui') -jest.unmock('../../ducks/views/main') +jest.unmock('../../ducks/flows') -import _ from 'lodash' -import thunk from 'redux-thunk' -import { applyMiddleware, createStore, combineReducers } from 'redux' -import reducer, { setActiveMenu, selectTabRelative } from '../../ducks/ui' -import { SELECT } from '../../ducks/views/main' +import reducer, { setActiveMenu } from '../../ducks/ui' +import * as flowActions from '../../ducks/flows'  describe('ui reducer', () => {      it('should return the initial state', () => { @@ -20,69 +14,23 @@ describe('ui reducer', () => {      })      it('should change the state to Start when deselecting a flow and we a currently at the flow tab', () => { -        expect(reducer({ activeMenu: 'Flow' }, { -            type: SELECT, -            currentSelection: 1, -            flowId : undefined, -        }).activeMenu).toEqual('Start') +        expect(reducer( +            { activeMenu: 'Flow', isFlowSelected: true }, +            flowActions.select(undefined)).activeMenu +        ).toEqual('Start')      })      it('should change the state to Flow when we selected a flow and no flow was selected before', () => { -        expect(reducer({ activeMenu: 'Start' }, { -            type: SELECT, -            currentSelection: undefined, -            flowId : 1, -        }).activeMenu).toEqual('Flow') +        expect(reducer( +            { activeMenu: 'Start', isFlowSelected: false }, +            flowActions.select(1)).activeMenu +        ).toEqual('Flow')      })      it('should not change the state to Flow when OPTIONS tab is selected and we selected a flow and a flow as selected before', () => { -        expect(reducer({activeMenu: 'Options'}, { -            type: SELECT, -            currentSelection: 1, -            flowId : '2', -        }).activeMenu).toEqual('Options') -    }) - -    describe('select tab relative', () => { - -        it('should select tab according to flow properties', () => { -            const store = createTestStore(makeState([{ id: 1 }], 1)) -            store.dispatch(selectTabRelative(1)) -            expect(store.getState().ui.panel).toEqual('details') -        }) - -        it('should select last tab when first tab is selected', () => { -            const store = createTestStore(makeState([{ id: 1, request: true, response: true, error: true }], 1)) -            store.dispatch(selectTabRelative(-1)) -            expect(store.getState().ui.panel).toEqual('details') -        }) - +        expect(reducer( +            { activeMenu: 'Options', isFlowSelected: true }, +            flowActions.select(1) +        ).activeMenu).toEqual('Options')      })  }) - -function createTestStore(state) { -    return createStore( -        combineReducers({ ui: reducer, flows: (state = {}) => state }), -        state, -        applyMiddleware(thunk) -    ) -} - - -// TODO: We should not duplicate our reducer logic here. -function makeState(flows, selected) { -    return { -        flows: { -            list: { -                data: flows, -                byId: _.fromPairs(flows.map(flow => [flow.id, flow])), -                indexOf: _.fromPairs(flows.map((flow, index) => [flow.id, index])), -            }, -            views: { -                main: { -                    selected: [selected], -                }, -            }, -        }, -    } -} diff --git a/web/src/js/__tests__/ducks/utils/list.js b/web/src/js/__tests__/ducks/utils/list.js index 8cae91ec..72d162f2 100644 --- a/web/src/js/__tests__/ducks/utils/list.js +++ b/web/src/js/__tests__/ducks/utils/list.js @@ -27,7 +27,7 @@ describe('list reduce', () => {              { id: 1, val: 1 },              { id: 2, val: 3 }          ]) -        expect(reduce(state, list.update(2, { id: 2, val: 3 }))).toEqual(result) +        expect(reduce(state, list.update({ id: 2, val: 3 }))).toEqual(result)      })      it('should remove item', () => { diff --git a/web/src/js/__tests__/ducks/utils/view.js b/web/src/js/__tests__/ducks/utils/view.js index 1b07f723..f0b147da 100644 --- a/web/src/js/__tests__/ducks/utils/view.js +++ b/web/src/js/__tests__/ducks/utils/view.js @@ -14,7 +14,7 @@ describe('view reduce', () => {          const result = createState([              { id: 1 }          ]) -        expect(reduce(state, view.updateFilter(state, item => item.id === 1))).toEqual(result) +        expect(reduce(state, view.updateFilter(state.data, item => item.id === 1))).toEqual(result)      })      it('should sort items', () => { @@ -72,7 +72,7 @@ describe('view reduce', () => {              { id: 1, val: 1 },              { id: 2, val: 3 }          ]) -        expect(reduce(state, view.update(2, { id: 2, val: 3 }))).toEqual(result) +        expect(reduce(state, view.update({ id: 2, val: 3 }))).toEqual(result)      })      it('should sort updated item', () => { @@ -84,7 +84,7 @@ describe('view reduce', () => {              { id: 2, val: 3 },              { id: 1, val: 1 }          ]) -        expect(reduce(state, view.update(2, { id: 2, val: 3 }, undefined, (a, b) => b.id - a.id))).toEqual(result) +        expect(reduce(state, view.update({ id: 2, val: 3 }, undefined, (a, b) => b.id - a.id))).toEqual(result)      })      it('should filter updated item', () => { @@ -96,7 +96,7 @@ describe('view reduce', () => {              { id: 1, val: 1 }          ])          result.indexOf[2] = null -        expect(reduce(state, view.update(2, { id: 2, val: 3 }, i => i.id === i.val))).toEqual(result) +        expect(reduce(state, view.update({ id: 2, val: 3 }, i => i.id === i.val))).toEqual(result)      })      it('should remove item', () => { @@ -119,7 +119,7 @@ describe('view reduce', () => {          const result = createState([              { id: 1 }          ]) -        expect(reduce(state, view.receive({ data: [{ id: 1 }] }))).toEqual(result) +        expect(reduce(state, view.receive([{ id: 1 }]))).toEqual(result)      })      it('should sort received items', () => { @@ -131,7 +131,7 @@ describe('view reduce', () => {              { id: 2 },              { id: 1 }          ]) -        expect(reduce(state, view.receive({ data: [{ id: 1 }, { id: 2 }] }, undefined, (a, b) => b.id - a.id))).toEqual(result) +        expect(reduce(state, view.receive([{ id: 1 }, { id: 2 }], undefined, (a, b) => b.id - a.id))).toEqual(result)      })      it('should filter received', () => { @@ -142,7 +142,7 @@ describe('view reduce', () => {          const result = createState([              { id: 1 }          ]) -        expect(reduce(state, view.receive({ data: [{ id: 1 }, { id: 2 }] }, i => i.id === 1))).toEqual(result) +        expect(reduce(state, view.receive([{ id: 1 }, { id: 2 }], i => i.id === 1))).toEqual(result)      })  }) diff --git a/web/src/js/__tests__/ducks/views/main.js b/web/src/js/__tests__/ducks/views/main.js deleted file mode 100644 index 0255f6ce..00000000 --- a/web/src/js/__tests__/ducks/views/main.js +++ /dev/null @@ -1,83 +0,0 @@ -jest.unmock('../../../ducks/views/main'); -jest.unmock('../../../ducks/utils/view'); -jest.unmock('redux-thunk') -jest.unmock('redux') - -import reduce, { selectRelative } from '../../../ducks/views/main'; -import thunk from 'redux-thunk' -import { applyMiddleware, createStore, combineReducers } from 'redux' - -describe('main reduce', () => { - -    describe('select previous', () => { - -        it('should not changed when first flow is selected', () => { -            const flows = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }] -            const store = createTestStore(makeState(flows, 1)) -            store.dispatch(selectRelative(-1)) -            expect(store.getState().flows.views.main.selected).toEqual([1]) -        }) - -        it('should select last flow if no flow is selected', () => { -            const flows = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }] -            const store = createTestStore(makeState(flows)) -            store.dispatch(selectRelative(-1)) -            expect(store.getState().flows.views.main.selected).toEqual([4]) -        }) - -    }) - -    describe('select next', () => { - -        it('should not change when last flow is selected', () => { -            const flows = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }] -            const store = createTestStore(makeState(flows, 4)) -            store.dispatch(selectRelative(1)) -            expect(store.getState().flows.views.main.selected).toEqual([4]) -        }) - -        it('should select first flow if no flow is selected', () => { -            const flows = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }] -            const store = createTestStore(makeState(flows, 1)) -            store.dispatch(selectRelative(1)) -            expect(store.getState().flows.views.main.selected).toEqual([2]) -        }) - -    }) -}) - -function createTestStore(defaultState) { -    return createStore( -        (state = defaultState, action) => ({ -            flows: { -                ...state.flows, -                views: { -                    main: reduce(state.flows.views.main, action) -                } -            } -        }), -        defaultState, -        applyMiddleware(thunk) -    ) -} - -// TODO: We should not duplicate our reducer logic here. -function makeState(flows, selected) { -    const list = { -        data: flows, -        byId: _.fromPairs(flows.map(flow => [flow.id, flow])), -        indexOf: _.fromPairs(flows.map((flow, index) => [flow.id, index])), -    } - -    return { -        flows: { -            list, -            views: { -                main: { -                    selected: [selected], -                    view: list, -                } -            } -        } -    } -} diff --git a/web/src/js/app.jsx b/web/src/js/app.jsx index bd2d3d58..726b2ae1 100644 --- a/web/src/js/app.jsx +++ b/web/src/js/app.jsx @@ -12,7 +12,7 @@ import { add as addLog } from './ducks/eventLog'  const middlewares = [thunk]; -if (process.env.NODE_ENV === 'development') { +if (process.env.NODE_ENV !== 'production') {    const createLogger = require('redux-logger');    middlewares.push(createLogger());  } diff --git a/web/src/js/components/FlowTable/FlowTableHead.jsx b/web/src/js/components/FlowTable/FlowTableHead.jsx index f65369bd..50242737 100644 --- a/web/src/js/components/FlowTable/FlowTableHead.jsx +++ b/web/src/js/components/FlowTable/FlowTableHead.jsx @@ -3,7 +3,7 @@ import { connect } from 'react-redux'  import classnames from 'classnames'  import columns from './FlowColumns' -import { updateSort } from '../../ducks/views/main' +import { updateSort } from '../../ducks/flowView'  FlowTableHead.propTypes = {      updateSort: PropTypes.func.isRequired, @@ -29,8 +29,8 @@ function FlowTableHead({ sortColumn, sortDesc, updateSort }) {  export default connect(      state => ({ -        sortDesc: state.flows.views.main.sort.desc, -        sortColumn: state.flows.views.main.sort.column, +        sortDesc: state.flowView.sort.desc, +        sortColumn: state.flowView.sort.column,      }),      {          updateSort diff --git a/web/src/js/components/Header.jsx b/web/src/js/components/Header.jsx index 7f1fa69f..5de885ae 100644 --- a/web/src/js/components/Header.jsx +++ b/web/src/js/components/Header.jsx @@ -17,10 +17,10 @@ class Header extends Component {      }      render() { -        const { query, selectedFlow, activeMenu} = this.props +        const { query, selectedFlowId, activeMenu} = this.props          let entries = [...Header.entries] -        if(selectedFlow) +        if(selectedFlowId)              entries.push(FlowMenu)          const Active = _.find(entries, (e) => e.title == activeMenu) @@ -51,7 +51,7 @@ class Header extends Component {  export default connect(      state => ({ -        selectedFlow: state.flows.views.main.selected[0], +        selectedFlowId: state.flows.selected[0],          activeMenu: state.ui.activeMenu,      }),      { diff --git a/web/src/js/components/Header/FlowMenu.jsx b/web/src/js/components/Header/FlowMenu.jsx index 9855cde3..bdd30d5e 100644 --- a/web/src/js/components/Header/FlowMenu.jsx +++ b/web/src/js/components/Header/FlowMenu.jsx @@ -29,7 +29,7 @@ function FlowMenu({ flow, acceptFlow, replayFlow, duplicateFlow, removeFlow, rev  export default connect(      state => ({ -        flow: state.flows.list.byId[state.flows.views.main.selected[0]], +        flow: state.flows.byId[state.flows.selected[0]],      }),      {          acceptFlow: flowsActions.accept, diff --git a/web/src/js/components/MainView.jsx b/web/src/js/components/MainView.jsx index 756fa22e..b0bbf70e 100644 --- a/web/src/js/components/MainView.jsx +++ b/web/src/js/components/MainView.jsx @@ -1,12 +1,11 @@  import React, { Component, PropTypes } from 'react'  import { connect } from 'react-redux'  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' +import { updateFilter, updateHighlight } from '../ducks/flowView'  class MainView extends Component { @@ -57,13 +56,13 @@ class MainView extends Component {  export default connect(      state => ({ -        flows: state.flows.views.main.view.data, -        filter: state.flows.views.main.filter, -        highlight: state.flows.views.main.highlight, -        selectedFlow: state.flows.list.byId[state.flows.views.main.selected[0]] +        flows: state.flowView.data, +        filter: state.flowView.filter, +        highlight: state.flowView.highlight, +        selectedFlow: state.flows.byId[state.flows.selected[0]]      }),      { -        selectFlow, +        selectFlow: flowsActions.select,          updateFilter,          updateHighlight,          updateFlow: flowsActions.update, diff --git a/web/src/js/components/ProxyApp.jsx b/web/src/js/components/ProxyApp.jsx index f0e33330..2962fc2b 100644 --- a/web/src/js/components/ProxyApp.jsx +++ b/web/src/js/components/ProxyApp.jsx @@ -60,7 +60,7 @@ export default connect(          showEventLog: state.eventLog.visible,          query: state.ui.query,          panel: state.ui.panel, -        selectedFlowId: state.flows.views.main.selected[0] +        selectedFlowId: state.flows.selected[0]      }),      {          appInit, diff --git a/web/src/js/components/ValueEditor/EditorBase.jsx b/web/src/js/components/ValueEditor/EditorBase.jsx index e737d2af..aa09dad5 100755 --- a/web/src/js/components/ValueEditor/EditorBase.jsx +++ b/web/src/js/components/ValueEditor/EditorBase.jsx @@ -54,7 +54,6 @@ export default class EditorBase extends Component {      render() {          return (              <this.props.tag -                {...this.props}                  tabIndex="0"                  className={`inline-input ${this.props.className}`}                  contentEditable={this.state.editable || undefined} diff --git a/web/src/js/ducks/eventLog.js b/web/src/js/ducks/eventLog.js index 52eec1cd..f72d7bd6 100644 --- a/web/src/js/ducks/eventLog.js +++ b/web/src/js/ducks/eventLog.js @@ -35,7 +35,7 @@ export default function reduce(state = defaultState, action) {              return {                  ...state,                  filters, -                view: reduceView(state.view, viewActions.updateFilter(state.list, log => filters[log.level])), +                view: reduceView(state.view, viewActions.updateFilter(state.list.data, log => filters[log.level])),              }          case ADD: @@ -55,7 +55,7 @@ export default function reduce(state = defaultState, action) {              return {                  ...state,                  list: reduceList(state.list, listActions.receive(action.list)), -                view: reduceView(state.view, viewActions.receive(list, log => state.filters[log.level])), +                view: reduceView(state.view, viewActions.receive(action.list, log => state.filters[log.level])),              }          default: diff --git a/web/src/js/ducks/views/main.js b/web/src/js/ducks/flowView.js index 0c401716..dd5bea41 100755..100644 --- a/web/src/js/ducks/views/main.js +++ b/web/src/js/ducks/flowView.js @@ -1,12 +1,12 @@ -import Filt from '../../filt/filt' -import { RequestUtils } from '../../flow/utils' -import reduceView, * as viewActions from '../utils/view' -import * as viewsActions from '../views' +import reduceView, * as viewActions from './utils/view' +import * as flowActions from './flows' +import Filt from '../filt/filt' +import { RequestUtils } from '../flow/utils' + +export const UPDATE_FILTER = 'FLOWVIEW_UPDATE_FILTER' +export const UPDATE_SORT = 'FLOWVIEW_UPDATE_SORT' +export const UPDATE_HIGHLIGHT = 'FLOWVIEW_UPDATE_HIGHLIGHT' -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'  const sortKeyFuns = { @@ -29,12 +29,37 @@ const sortKeyFuns = {      },  } +export function makeFilter(filter) { +    if (!filter) { +        return +    } +    return Filt.parse(filter) +} + +export function makeSort({ column, desc }) { +    const sortKeyFun = sortKeyFuns[column] +    if (!sortKeyFun) { +        return +    } +    return (a, b) => { +        const ka = sortKeyFun(a) +        const kb = sortKeyFun(b) +        if (ka > kb) { +            return desc ? -1 : 1 +        } +        if (ka < kb) { +            return desc ? 1 : -1 +        } +        return 0 +    } +} + +  const defaultState = {      highlight: null, -    selected: [],      filter: null,      sort: { column: null, desc: false }, -    view: undefined, +    ...reduceView(undefined, {})  }  export default function reduce(state = defaultState, action) { @@ -46,44 +71,35 @@ export default function reduce(state = defaultState, action) {                  highlight: action.highlight,              } -        case SELECT: -            return { -                ...state, -                selected: [action.id] -            } -          case UPDATE_FILTER:              return { -                ...state, -                filter: action.filter, -                view: reduceView( -                    state.view, +                ...reduceView( +                    state,                      viewActions.updateFilter( -                        action.list, +                        action.flows,                          makeFilter(action.filter),                          makeSort(state.sort)                      )                  ), +                filter: action.filter,              }          case UPDATE_SORT:              const sort = { column: action.column, desc: action.desc }              return { -                ...state, -                sort, -                view: reduceView( -                    state.view, +                ...reduceView( +                    state,                      viewActions.updateSort(                          makeSort(sort)                      )                  ), +                sort,              } -        case viewsActions.ADD: +        case flowActions.ADD:              return { -                ...state, -                view: reduceView( -                    state.view, +                ...reduceView( +                    state,                      viewActions.add(                          action.item,                          makeFilter(state.filter), @@ -92,13 +108,11 @@ export default function reduce(state = defaultState, action) {                  ),              } -        case viewsActions.UPDATE: +        case flowActions.UPDATE:              return { -                ...state, -                view: reduceView( -                    state.view, +                ...reduceView( +                    state,                      viewActions.update( -                        action.id,                          action.item,                          makeFilter(state.filter),                          makeSort(state.sort) @@ -106,22 +120,20 @@ export default function reduce(state = defaultState, action) {                  ),              } -        case viewsActions.REMOVE: +        case flowActions.REMOVE:              return { -                ...state, -                view: reduceView( -                    state.view, +                ...reduceView( +                    state,                      viewActions.remove(                          action.id                      )                  ),              } -        case viewsActions.RECEIVE: +        case flowActions.RECEIVE:              return { -                ...state, -                view: reduceView( -                    state.view, +                ...reduceView( +                    state,                      viewActions.receive(                          action.list,                          makeFilter(state.filter), @@ -132,8 +144,7 @@ export default function reduce(state = defaultState, action) {          default:              return { -                ...state, -                view: reduceView(state.view, action) +                ...reduceView(state, action),              }      }  } @@ -143,7 +154,7 @@ export default function reduce(state = defaultState, action) {   */  export function updateFilter(filter) {      return (dispatch, getState) => { -        dispatch({ type: UPDATE_FILTER, filter, list: getState().flows.list }) +        dispatch({ type: UPDATE_FILTER, filter, flows: getState().flows.data })      }  } @@ -161,62 +172,24 @@ export function updateSort(column, desc) {      return { type: UPDATE_SORT, column, desc }  } -/** - * @public - */ -export function select(id) { -    return (dispatch, getState) => { -        dispatch({ type: SELECT, currentSelection: getState().flows.views.main.selected[0], id }) -    } -}  /**   * @public   */  export function selectRelative(shift) {      return (dispatch, getState) => { -        let currentSelection = getState().flows.views.main.selected[0] -        let id -        if (shift === null){ -            id = null -        } else if (!currentSelection) { -            id = (action.shift < 0) ? 0 : state.view.data.length - 1 +        let currentSelectionIndex = getState().flowView.indexOf[getState().flows.selected[0]] +        let minIndex = 0 +        let maxIndex = getState().flowView.data.length - 1 +        let newIndex +        if (currentSelectionIndex === undefined) { +            newIndex = (shift < 0) ? minIndex : maxIndex          } else { -            id = state.view.indexOf[currentSelection] + action.shift -            id = Math.max(id, 0) -            id = Math.min(id, state.view.data.length - 1) +            newIndex = currentSelectionIndex + shift +            newIndex = Math.max(newIndex, minIndex) +            newIndex = Math.min(newIndex, maxIndex)          } -        dispatch({ type: SELECT, currentSelection, id }) -    } -} - -/** - * @private - */ -function makeFilter(filter) { -    if (!filter) { -        return -    } -    return Filt.parse(filter) -} - -/** - * @private - */ -function makeSort({ column, desc }) { -    const sortKeyFun = sortKeyFuns[column] -    if (!sortKeyFun) { -        return -    } -    return (a, b) => { -        const ka = sortKeyFun(a) -        const kb = sortKeyFun(b) -        if (ka > kb) { -            return desc ? -1 : 1 -        } -        if (ka < kb) { -            return desc ? 1 : -1 -        } -        return 0 +        let flow = getState().flowView.data[newIndex] +        dispatch(flowActions.select(flow ? flow.id : undefined))      }  } diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js index f0b09530..dfcd5ba9 100644 --- a/web/src/js/ducks/flows.js +++ b/web/src/js/ducks/flows.js @@ -1,23 +1,24 @@  import { fetchApi } from '../utils'  import reduceList, * as listActions from './utils/list' -import reduceViews, * as viewsActions from './views' +  import * as msgQueueActions from './msgQueue'  import * as websocketActions from './websocket'  export const MSG_TYPE = 'UPDATE_FLOWS'  export const DATA_URL = '/flows' -export const ADD            = 'FLOWS_ADD' -export const UPDATE         = 'FLOWS_UPDATE' -export const REMOVE         = 'FLOWS_REMOVE' -export const RECEIVE        = 'FLOWS_RECEIVE' +export const ADD = 'FLOWS_ADD' +export const UPDATE = 'FLOWS_UPDATE' +export const REMOVE = 'FLOWS_REMOVE' +export const RECEIVE = 'FLOWS_RECEIVE'  export const REQUEST_ACTION = 'FLOWS_REQUEST_ACTION' -export const UNKNOWN_CMD    = 'FLOWS_UNKNOWN_CMD' -export const FETCH_ERROR    = 'FLOWS_FETCH_ERROR' +export const UNKNOWN_CMD = 'FLOWS_UNKNOWN_CMD' +export const FETCH_ERROR = 'FLOWS_FETCH_ERROR' +export const SELECT = 'FLOWS_SELECT'  const defaultState = { -    list: undefined, -    views: undefined, +    selected: [], +    ...reduceList(undefined, {}),  }  export default function reduce(state = defaultState, action) { @@ -26,37 +27,37 @@ export default function reduce(state = defaultState, action) {          case ADD:              return {                  ...state, -                list: reduceList(state.list, listActions.add(action.item)), -                views: reduceViews(state.views, viewsActions.add(action.item)), +                ...reduceList(state, listActions.add(action.item)),              }          case UPDATE:              return {                  ...state, -                list: reduceList(state.list, listActions.update(action.id, action.item)), -                views: reduceViews(state.views, viewsActions.update(action.id, action.item)), +                ...reduceList(state, listActions.update(action.item)),              }          case REMOVE:              return {                  ...state, -                list: reduceList(state.list, listActions.remove(action.id)), -                views: reduceViews(state.views, viewsActions.remove(action.id)), +                ...reduceList(state, listActions.remove(action.id)),              }          case RECEIVE: -            const list = reduceList(state.list, listActions.receive(action.list))              return {                  ...state, -                list, -                views: reduceViews(state.views, viewsActions.receive(list)), +                ...reduceList(state, listActions.receive(action.list)), +            } + +        case SELECT: +            return { +                ...state, +                selected: action.flowIds              }          default:              return {                  ...state, -                list: reduceList(state.list, action), -                views: reduceViews(state.views, action), +                ...reduceList(state, action),              }      }  } @@ -143,6 +144,15 @@ export function upload(file) {      return { type: REQUEST_ACTION }  } + +export function select(id) { +    return { +        type: SELECT, +        flowIds: id ? [id] : [] +    } +} + +  /**   * This action creater takes all WebSocket events   * @@ -152,16 +162,16 @@ export function handleWsMsg(msg) {      switch (msg.cmd) {          case websocketActions.CMD_ADD: -            return addItem(msg.data) +            return addFlow(msg.data)          case websocketActions.CMD_UPDATE: -            return updateItem(msg.data.id, msg.data) +            return updateFlow(msg.data)          case websocketActions.CMD_REMOVE: -            return removeItem(msg.data.id) +            return removeFlow(msg.data.id)          case websocketActions.CMD_RESET: -            return fetchData() +            return fetchFlows()          default:              return { type: UNKNOWN_CMD, msg } @@ -171,7 +181,7 @@ export function handleWsMsg(msg) {  /**   * @public websocket   */ -export function fetchData() { +export function fetchFlows() {      return msgQueueActions.fetchData(MSG_TYPE)  } @@ -185,20 +195,20 @@ export function receiveData(list) {  /**   * @private   */ -export function addItem(item) { +export function addFlow(item) {      return { type: ADD, item }  }  /**   * @private   */ -export function updateItem(id, item) { -    return { type: UPDATE, id, item } +export function updateFlow(item) { +    return { type: UPDATE, item }  }  /**   * @private   */ -export function removeItem(id) { +export function removeFlow(id) {      return { type: REMOVE, id }  } diff --git a/web/src/js/ducks/index.js b/web/src/js/ducks/index.js index c2488d70..16193530 100644 --- a/web/src/js/ducks/index.js +++ b/web/src/js/ducks/index.js @@ -2,6 +2,7 @@ import { combineReducers } from 'redux'  import eventLog from './eventLog'  import websocket from './websocket'  import flows from './flows' +import flowView from './flowView'  import settings from './settings'  import ui from './ui'  import msgQueue from './msgQueue' @@ -10,6 +11,7 @@ export default combineReducers({      eventLog,      websocket,      flows, +    flowView,      settings,      ui,      msgQueue, diff --git a/web/src/js/ducks/ui.js b/web/src/js/ducks/ui.js index 15334f88..f745f0af 100644 --- a/web/src/js/ducks/ui.js +++ b/web/src/js/ducks/ui.js @@ -1,18 +1,18 @@ -import { SELECT as SELECT_FLOW, selectRelative as selectFlowRelative } from './views/main' +import { selectRelative as selectFlowRelative } from './flowView'  import { Key } from '../utils.js' -import * as flowsActions from '../ducks/flows' +import * as flowsActions from './flows'  export const SET_ACTIVE_MENU = 'UI_SET_ACTIVE_MENU'  export const SET_CONTENT_VIEW = 'UI_SET_CONTENT_VIEW'  export const SET_SELECTED_INPUT = 'UI_SET_SELECTED_INPUT'  export const UPDATE_QUERY = 'UI_UPDATE_QUERY'  export const SELECT_TAB = 'UI_SELECT_TAB' -export const SELECT_TAB_RELATIVE = 'UI_SELECT_TAB_RELATIVE'  export const SET_PROMPT = 'UI_SET_PROMPT'  export const SET_DISPLAY_LARGE = 'UI_SET_DISPLAY_LARGE'  const defaultState = {      activeMenu: 'Start', +    isFlowSelected: false,      selectedInput: null,      displayLarge: false,      promptOpen: false, @@ -30,20 +30,25 @@ export default function reducer(state = defaultState, action) {                  activeMenu: action.activeMenu,              } -        case SELECT_FLOW: -            if (action.flowId && !action.currentSelection) { +        case flowsActions.SELECT: +            if (action.flowIds.length && !state.isFlowSelected) {                  return {                      ...state,                      displayLarge: false,                      activeMenu: 'Flow', +                    isFlowSelected: true,                  }              } -            if (!action.flowId && state.activeMenu === 'Flow') { +            if (!action.flowIds.length && state.isFlowSelected) { +                let activeMenu = state.activeMenu +                if (activeMenu == 'Flow') { +                    activeMenu = 'Start' +                }                  return {                      ...state, -                    displayLarge: false, -                    activeMenu: 'Start', +                    activeMenu, +                    isFlowSelected: false,                  }              } @@ -76,19 +81,6 @@ export default function reducer(state = defaultState, action) {                  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, @@ -126,13 +118,6 @@ 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 }  } @@ -142,13 +127,17 @@ export function setDisplayLarge(displayLarge) {  }  export function onKeyDown(e) { -    if(e.ctrlKey) { -        return () => {} +    if (e.ctrlKey) { +        return () => { +        }      }      var key = e.keyCode      var shiftKey = e.shiftKey      e.preventDefault()      return (dispatch, getState) => { + +        const flow = getState().flows.byId[getState().flows.selected[0]] +          switch (key) {              case Key.I: @@ -191,20 +180,27 @@ export function onKeyDown(e) {                  break              case Key.ESC: -                dispatch(selectFlowRelative(null)) -                dispatch(selectTabRelative(null)) +                dispatch(flowsActions.select(null))                  break -            case Key.H:              case Key.LEFT: -                dispatch(selectTabRelative(-1)) +            { +                let tabs = ['request', 'response', 'error'].filter(k => flow[k]).concat(['details']), +                    currentTab = getState().ui.panel, +                    nextTab = tabs[(tabs.indexOf(currentTab) - 1 + tabs.length) % tabs.length] +                dispatch(selectTab(nextTab))                  break +            } -            case Key.L:              case Key.TAB:              case Key.RIGHT: -                dispatch(selectTabRelative(+1)) +            { +                let tabs = ['request', 'response', 'error'].filter(k => flow[k]).concat(['details']), +                    currentTab = getState().ui.panel, +                    nextTab = tabs[(tabs.indexOf(currentTab) + 1) % tabs.length] +                dispatch(selectTab(nextTab))                  break +            }              case Key.C:                  if (shiftKey) { @@ -212,8 +208,8 @@ export function onKeyDown(e) {                  }                  break -            case Key.D: { -                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +            case Key.D: +            {                  if (!flow) {                      return                  } @@ -225,8 +221,8 @@ export function onKeyDown(e) {                  break              } -            case Key.A: { -                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +            case Key.A: +            {                  if (shiftKey) {                      dispatch(flowsActions.acceptAll())                  } else if (flow && flow.intercepted) { @@ -235,16 +231,16 @@ export function onKeyDown(e) {                  break              } -            case Key.R: { -                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +            case Key.R: +            {                  if (!shiftKey && flow) {                      dispatch(flowsActions.replay(flow))                  }                  break              } -            case Key.V: { -                const flow = getState().flows.list.byId[getState().flows.views.main.selected[0]] +            case Key.V: +            {                  if (!shiftKey && flow && flow.modified) {                      dispatch(flowsActions.revert(flow))                  } @@ -256,7 +252,8 @@ export function onKeyDown(e) {                  break              default: -                return () => {} +                return () => { +                }          }      }  } diff --git a/web/src/js/ducks/utils/list.js b/web/src/js/ducks/utils/list.js index 4f631590..fdeb5856 100644 --- a/web/src/js/ducks/utils/list.js +++ b/web/src/js/ducks/utils/list.js @@ -23,7 +23,7 @@ export default function reduce(state = defaultState, action) {              }          case UPDATE: { -            const index = state.indexOf[action.id] +            const index = state.indexOf[action.item.id]              if (index == null) {                  return state @@ -36,7 +36,7 @@ export default function reduce(state = defaultState, action) {              return {                  ...state,                  data, -                byId: { ...state.byId, [action.id]: action.item } +                byId: { ...state.byId, [action.item.id]: action.item }              }          } @@ -86,8 +86,8 @@ export function add(item) {  /**   * @public   */ -export function update(id, item) { -    return { type: UPDATE, id, item } +export function update(item) { +    return { type: UPDATE, item }  }  /** diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js index 0349a398..c00f00bd 100755 --- a/web/src/js/ducks/utils/view.js +++ b/web/src/js/ducks/utils/view.js @@ -15,8 +15,9 @@ const defaultState = {  export default function reduce(state = defaultState, action) {      switch (action.type) { -        case UPDATE_FILTER: { -            const data = action.list.data.filter(action.filter).sort(action.sort) +        case UPDATE_FILTER: +        { +            const data = action.list.filter(action.filter).sort(action.sort)              return {                  ...state,                  data, @@ -24,7 +25,8 @@ export default function reduce(state = defaultState, action) {              }          } -        case UPDATE_SORT: { +        case UPDATE_SORT: +        {              const data = [...state.data].sort(action.sort)              return {                  ...state, @@ -51,13 +53,13 @@ export default function reduce(state = defaultState, action) {                  ...sortedRemove(state, action.id),              } -        case UPDATE: { -            if (state.indexOf[action.id] == null) { +        case UPDATE: +            if (state.indexOf[action.item.id] == null) {                  return              }              const nextState = {                  ...state, -                ...sortedRemove(state, action.id), +                ...sortedRemove(state, action.item.id),              }              if (!action.filter(action.item)) {                  return nextState @@ -66,10 +68,10 @@ export default function reduce(state = defaultState, action) {                  ...nextState,                  ...sortedInsert(nextState, action.item, action.sort)              } -        } -        case RECEIVE: { -            const data = action.list.data.filter(action.filter).sort(action.sort) +        case RECEIVE: +        { +            const data = action.list.filter(action.filter).sort(action.sort)              return {                  ...state,                  data, @@ -94,8 +96,8 @@ export function add(item, filter = defaultFilter, sort = defaultSort) {      return { type: ADD, item, filter, sort }  } -export function update(id, item, filter = defaultFilter, sort = defaultSort) { -    return { type: UPDATE, id, item, filter, sort } +export function update(item, filter = defaultFilter, sort = defaultSort) { +    return { type: UPDATE, item, filter, sort }  }  export function remove(id) { diff --git a/web/src/js/ducks/views.js b/web/src/js/ducks/views.js deleted file mode 100755 index e1e46c2e..00000000 --- a/web/src/js/ducks/views.js +++ /dev/null @@ -1,40 +0,0 @@ -import { combineReducers } from 'redux' -import * as viewActions from './utils/view' -import main from './views/main.js' - -export const ADD = 'FLOW_VIEWS_ADD' -export const UPDATE = 'FLOW_VIEWS_UPDATE' -export const REMOVE = 'FLOW_VIEWS_REMOVE' -export const RECEIVE = 'FLOW_VIEWS_RECEIVE' - -export default combineReducers({ -    main, -}) - -/** - * @public - */ -export function add(item) { -    return { type: ADD, item } -} - -/** - * @public - */ -export function update(id, item) { -    return { type: UPDATE, id, item } -} - -/** - * @public - */ -export function remove(id) { -    return { type: REMOVE, id } -} - -/** - * @public - */ -export function receive(list) { -    return { type: RECEIVE, list } -} diff --git a/web/src/js/ducks/websocket.js b/web/src/js/ducks/websocket.js index 5ba7baef..21400bb5 100644 --- a/web/src/js/ducks/websocket.js +++ b/web/src/js/ducks/websocket.js @@ -68,7 +68,7 @@ export function onConnect() {      return dispatch => {          dispatch({ type: CONNECTED })          dispatch(settingsActions.fetchData()) -        dispatch(flowsActions.fetchData()) +        dispatch(flowsActions.fetchFlows())          dispatch(eventLogActions.fetchData())      }  }  | 
