mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-29 11:03:13 +00:00
web: simplify flow storage
This commit is contained in:
parent
85476d9915
commit
c2a130dced
@ -251,7 +251,7 @@ class FlowHandler(RequestHandler):
|
|||||||
elif k == "port":
|
elif k == "port":
|
||||||
request.port = int(v)
|
request.port = int(v)
|
||||||
elif k == "headers":
|
elif k == "headers":
|
||||||
request.headers.set_state(v)
|
request.headers.set_state((a.encode(), b.encode()) for a, b in v)
|
||||||
elif k == "content":
|
elif k == "content":
|
||||||
request.text = v
|
request.text = v
|
||||||
else:
|
else:
|
||||||
|
@ -21,7 +21,8 @@ class Stop(Exception):
|
|||||||
|
|
||||||
class _WebState():
|
class _WebState():
|
||||||
def add_log(self, e, level):
|
def add_log(self, e, level):
|
||||||
self._last_event_id += 1
|
# server-side log ids are odd
|
||||||
|
self._last_event_id += 2
|
||||||
entry = {
|
entry = {
|
||||||
"id": self._last_event_id,
|
"id": self._last_event_id,
|
||||||
"message": e,
|
"message": e,
|
||||||
|
@ -48,9 +48,9 @@ export default class WebsocketBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
receive(resource, msg) {
|
receive(resource, data) {
|
||||||
let type = `${resource}_RECEIVE`.toUpperCase()
|
let type = `${resource}_RECEIVE`.toUpperCase()
|
||||||
this.store.dispatch({ type, [resource]: msg })
|
this.store.dispatch({ type, cmd: "receive", resource, data })
|
||||||
let queue = this.activeFetches[resource]
|
let queue = this.activeFetches[resource]
|
||||||
delete this.activeFetches[resource]
|
delete this.activeFetches[resource]
|
||||||
queue.forEach(msg => this.onMessage(msg))
|
queue.forEach(msg => this.onMessage(msg))
|
||||||
|
@ -3,15 +3,15 @@ import { connect } from 'react-redux'
|
|||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import columns from './FlowColumns'
|
import columns from './FlowColumns'
|
||||||
|
|
||||||
import { updateSort } from '../../ducks/flowView'
|
import { setSort } from '../../ducks/flows'
|
||||||
|
|
||||||
FlowTableHead.propTypes = {
|
FlowTableHead.propTypes = {
|
||||||
updateSort: PropTypes.func.isRequired,
|
setSort: PropTypes.func.isRequired,
|
||||||
sortDesc: React.PropTypes.bool.isRequired,
|
sortDesc: React.PropTypes.bool.isRequired,
|
||||||
sortColumn: React.PropTypes.string,
|
sortColumn: React.PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
function FlowTableHead({ sortColumn, sortDesc, updateSort }) {
|
function FlowTableHead({ sortColumn, sortDesc, setSort }) {
|
||||||
const sortType = sortDesc ? 'sort-desc' : 'sort-asc'
|
const sortType = sortDesc ? 'sort-desc' : 'sort-asc'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -19,7 +19,7 @@ function FlowTableHead({ sortColumn, sortDesc, updateSort }) {
|
|||||||
{columns.map(Column => (
|
{columns.map(Column => (
|
||||||
<th className={classnames(Column.headerClass, sortColumn === Column.name && sortType)}
|
<th className={classnames(Column.headerClass, sortColumn === Column.name && sortType)}
|
||||||
key={Column.name}
|
key={Column.name}
|
||||||
onClick={() => updateSort(Column.name, Column.name !== sortColumn ? false : !sortDesc)}>
|
onClick={() => setSort(Column.name, Column.name !== sortColumn ? false : !sortDesc)}>
|
||||||
{Column.headerName}
|
{Column.headerName}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
@ -29,10 +29,10 @@ function FlowTableHead({ sortColumn, sortDesc, updateSort }) {
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
sortDesc: state.flowView.sort.desc,
|
sortDesc: state.flows.sort.desc,
|
||||||
sortColumn: state.flowView.sort.column,
|
sortColumn: state.flows.sort.column,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
updateSort
|
setSort
|
||||||
}
|
}
|
||||||
)(FlowTableHead)
|
)(FlowTableHead)
|
||||||
|
@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react'
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import FilterInput from './FilterInput'
|
import FilterInput from './FilterInput'
|
||||||
import { update as updateSettings } from '../../ducks/settings'
|
import { update as updateSettings } from '../../ducks/settings'
|
||||||
import { updateFilter, updateHighlight } from '../../ducks/flowView'
|
import { setFilter, setHighlight } from '../../ducks/flows'
|
||||||
|
|
||||||
MainMenu.title = "Start"
|
MainMenu.title = "Start"
|
||||||
|
|
||||||
@ -31,20 +31,20 @@ const InterceptInput = connect(
|
|||||||
|
|
||||||
const FlowFilterInput = connect(
|
const FlowFilterInput = connect(
|
||||||
state => ({
|
state => ({
|
||||||
value: state.flowView.filter || '',
|
value: state.flows.filter || '',
|
||||||
placeholder: 'Search',
|
placeholder: 'Search',
|
||||||
type: 'search',
|
type: 'search',
|
||||||
color: 'black'
|
color: 'black'
|
||||||
}),
|
}),
|
||||||
{ onChange: updateFilter }
|
{ onChange: setFilter }
|
||||||
)(FilterInput);
|
)(FilterInput);
|
||||||
|
|
||||||
const HighlightInput = connect(
|
const HighlightInput = connect(
|
||||||
state => ({
|
state => ({
|
||||||
value: state.flowView.highlight || '',
|
value: state.flows.highlight || '',
|
||||||
placeholder: 'Highlight',
|
placeholder: 'Highlight',
|
||||||
type: 'tag',
|
type: 'tag',
|
||||||
color: 'hsl(48, 100%, 50%)'
|
color: 'hsl(48, 100%, 50%)'
|
||||||
}),
|
}),
|
||||||
{ onChange: updateHighlight }
|
{ onChange: setHighlight }
|
||||||
)(FilterInput);
|
)(FilterInput);
|
||||||
|
@ -4,7 +4,6 @@ import Splitter from './common/Splitter'
|
|||||||
import FlowTable from './FlowTable'
|
import FlowTable from './FlowTable'
|
||||||
import FlowView from './FlowView'
|
import FlowView from './FlowView'
|
||||||
import * as flowsActions from '../ducks/flows'
|
import * as flowsActions from '../ducks/flows'
|
||||||
import { updateFilter, updateHighlight } from '../ducks/flowView'
|
|
||||||
|
|
||||||
class MainView extends Component {
|
class MainView extends Component {
|
||||||
|
|
||||||
@ -41,16 +40,14 @@ class MainView extends Component {
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
flows: state.flowView.data,
|
flows: state.flows.view,
|
||||||
filter: state.flowView.filter,
|
filter: state.flows.filter,
|
||||||
highlight: state.flowView.highlight,
|
highlight: state.flows.highlight,
|
||||||
selectedFlow: state.flows.byId[state.flows.selected[0]],
|
selectedFlow: state.flows.byId[state.flows.selected[0]],
|
||||||
tab: state.ui.flow.tab,
|
tab: state.ui.flow.tab,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
selectFlow: flowsActions.select,
|
selectFlow: flowsActions.select,
|
||||||
updateFilter,
|
|
||||||
updateHighlight,
|
|
||||||
updateFlow: flowsActions.update,
|
updateFlow: flowsActions.update,
|
||||||
}
|
}
|
||||||
)(MainView)
|
)(MainView)
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
import reduceList, * as listActions from './utils/list'
|
import reduceStore from "./utils/store"
|
||||||
import reduceView, * as viewActions from './utils/view'
|
import * as storeActions from "./utils/store"
|
||||||
|
|
||||||
export const ADD = 'EVENTS_ADD'
|
export const ADD = 'EVENTS_ADD'
|
||||||
export const RECEIVE = 'EVENTS_RECEIVE'
|
export const RECEIVE = 'EVENTS_RECEIVE'
|
||||||
export const TOGGLE_VISIBILITY = 'EVENTS_TOGGLE_VISIBILITY'
|
export const TOGGLE_VISIBILITY = 'EVENTS_TOGGLE_VISIBILITY'
|
||||||
export const TOGGLE_FILTER = 'EVENTS_TOGGLE_FILTER'
|
export const TOGGLE_FILTER = 'EVENTS_TOGGLE_FILTER'
|
||||||
export const UNKNOWN_CMD = 'EVENTS_UNKNOWN_CMD'
|
|
||||||
export const FETCH_ERROR = 'EVENTS_FETCH_ERROR'
|
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
logId: 0,
|
|
||||||
visible: false,
|
visible: false,
|
||||||
filters: { debug: false, info: true, web: true },
|
filters: { debug: false, info: true, web: true },
|
||||||
list: reduceList(undefined, {}),
|
...reduceStore(undefined, {}),
|
||||||
view: reduceView(undefined, {}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function reduce(state = defaultState, action) {
|
export default function reduce(state = defaultState, action) {
|
||||||
@ -30,27 +26,14 @@ export default function reduce(state = defaultState, action) {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
filters,
|
filters,
|
||||||
view: reduceView(state.view, viewActions.updateFilter(state.list.data, log => filters[log.level])),
|
...reduceStore(state, storeActions.setFilter(log => filters[log.level]))
|
||||||
}
|
}
|
||||||
|
|
||||||
case ADD:
|
case ADD:
|
||||||
const item = {
|
|
||||||
id: state.logId,
|
|
||||||
message: action.message,
|
|
||||||
level: action.level,
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
logId: state.logId + 1,
|
|
||||||
list: reduceList(state.list, listActions.add(item)),
|
|
||||||
view: reduceView(state.view, viewActions.add(item, log => state.filters[log.level])),
|
|
||||||
}
|
|
||||||
|
|
||||||
case RECEIVE:
|
case RECEIVE:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
list: reduceList(state.list, listActions.receive(action.events)),
|
...reduceStore(state, storeActions[action.cmd](action.data, log => state.filters[log.level]))
|
||||||
view: reduceView(state.view, viewActions.receive(action.events, log => state.filters[log.level])),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -66,6 +49,17 @@ export function toggleVisibility() {
|
|||||||
return { type: TOGGLE_VISIBILITY }
|
return { type: TOGGLE_VISIBILITY }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let logId = 1 // client-side log ids are odd
|
||||||
export function add(message, level = 'web') {
|
export function add(message, level = 'web') {
|
||||||
return { type: ADD, message, level }
|
let data = {
|
||||||
|
id: logId,
|
||||||
|
message,
|
||||||
|
level,
|
||||||
|
}
|
||||||
|
logId += 2
|
||||||
|
return {
|
||||||
|
type: ADD,
|
||||||
|
cmd: "add",
|
||||||
|
data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,192 +0,0 @@
|
|||||||
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'
|
|
||||||
|
|
||||||
|
|
||||||
const sortKeyFuns = {
|
|
||||||
|
|
||||||
TLSColumn: flow => flow.request.scheme,
|
|
||||||
|
|
||||||
PathColumn: flow => RequestUtils.pretty_url(flow.request),
|
|
||||||
|
|
||||||
MethodColumn: flow => flow.request.method,
|
|
||||||
|
|
||||||
StatusColumn: flow => flow.response && flow.response.status_code,
|
|
||||||
|
|
||||||
TimeColumn: flow => flow.response && flow.response.timestamp_end - flow.request.timestamp_start,
|
|
||||||
|
|
||||||
SizeColumn: flow => {
|
|
||||||
let total = flow.request.contentLength
|
|
||||||
if (flow.response) {
|
|
||||||
total += flow.response.contentLength || 0
|
|
||||||
}
|
|
||||||
return total
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
filter: null,
|
|
||||||
sort: { column: null, desc: false },
|
|
||||||
...reduceView(undefined, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function reduce(state = defaultState, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case UPDATE_HIGHLIGHT:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
highlight: action.highlight,
|
|
||||||
}
|
|
||||||
|
|
||||||
case UPDATE_FILTER:
|
|
||||||
return {
|
|
||||||
...reduceView(
|
|
||||||
state,
|
|
||||||
viewActions.updateFilter(
|
|
||||||
action.flows,
|
|
||||||
makeFilter(action.filter),
|
|
||||||
makeSort(state.sort)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
filter: action.filter,
|
|
||||||
}
|
|
||||||
|
|
||||||
case UPDATE_SORT:
|
|
||||||
const sort = { column: action.column, desc: action.desc }
|
|
||||||
return {
|
|
||||||
...reduceView(
|
|
||||||
state,
|
|
||||||
viewActions.updateSort(
|
|
||||||
makeSort(sort)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
sort,
|
|
||||||
}
|
|
||||||
|
|
||||||
case flowActions.ADD:
|
|
||||||
return {
|
|
||||||
...reduceView(
|
|
||||||
state,
|
|
||||||
viewActions.add(
|
|
||||||
action.item,
|
|
||||||
makeFilter(state.filter),
|
|
||||||
makeSort(state.sort)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case flowActions.UPDATE:
|
|
||||||
return {
|
|
||||||
...reduceView(
|
|
||||||
state,
|
|
||||||
viewActions.update(
|
|
||||||
action.item,
|
|
||||||
makeFilter(state.filter),
|
|
||||||
makeSort(state.sort)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case flowActions.REMOVE:
|
|
||||||
/* FIXME: Implement select switch on remove
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
let currentIndex = getState().flowView.indexOf[getState().flows.selected[0]]
|
|
||||||
let maxIndex = getState().flowView.data.length - 1
|
|
||||||
let deleteLastEntry = maxIndex == 0
|
|
||||||
if (deleteLastEntry)
|
|
||||||
dispatch(select())
|
|
||||||
else
|
|
||||||
dispatch(selectRelative(currentIndex == maxIndex ? -1 : 1) )
|
|
||||||
*/
|
|
||||||
return {
|
|
||||||
...reduceView(
|
|
||||||
state,
|
|
||||||
viewActions.remove(
|
|
||||||
action.id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case flowActions.RECEIVE:
|
|
||||||
return {
|
|
||||||
...reduceView(
|
|
||||||
state,
|
|
||||||
viewActions.receive(
|
|
||||||
action.flows,
|
|
||||||
makeFilter(state.filter),
|
|
||||||
makeSort(state.sort)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
...reduceView(state, action),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateFilter(filter) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
dispatch({ type: UPDATE_FILTER, filter, flows: getState().flows.data })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateHighlight(highlight) {
|
|
||||||
return { type: UPDATE_HIGHLIGHT, highlight }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSort(column, desc) {
|
|
||||||
return { type: UPDATE_SORT, column, desc }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function selectRelative(shift) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
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 {
|
|
||||||
newIndex = currentSelectionIndex + shift
|
|
||||||
newIndex = Math.max(newIndex, minIndex)
|
|
||||||
newIndex = Math.min(newIndex, maxIndex)
|
|
||||||
}
|
|
||||||
let flow = getState().flowView.data[newIndex]
|
|
||||||
dispatch(flowActions.select(flow ? flow.id : undefined))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +1,58 @@
|
|||||||
import { fetchApi } from '../utils'
|
import { fetchApi } from "../utils"
|
||||||
import reduceList, * as listActions from './utils/list'
|
import reduceStore from "./utils/store"
|
||||||
import { selectRelative } from './flowView'
|
import * as storeActions from "./utils/store"
|
||||||
|
import Filt from "../filt/filt"
|
||||||
|
import { RequestUtils } from "../flow/utils"
|
||||||
|
|
||||||
export const ADD = 'FLOWS_ADD'
|
export const ADD = 'FLOWS_ADD'
|
||||||
export const UPDATE = 'FLOWS_UPDATE'
|
export const UPDATE = 'FLOWS_UPDATE'
|
||||||
export const REMOVE = 'FLOWS_REMOVE'
|
export const REMOVE = 'FLOWS_REMOVE'
|
||||||
export const RECEIVE = 'FLOWS_RECEIVE'
|
export const RECEIVE = 'FLOWS_RECEIVE'
|
||||||
|
export const SELECT = 'FLOWS_SELECT'
|
||||||
|
export const SET_FILTER = 'FLOWS_SET_FILTER'
|
||||||
|
export const SET_SORT = 'FLOWS_SET_SORT'
|
||||||
|
export const SET_HIGHLIGHT = 'FLOWS_SET_HIGHLIGHT'
|
||||||
export const REQUEST_ACTION = 'FLOWS_REQUEST_ACTION'
|
export const REQUEST_ACTION = 'FLOWS_REQUEST_ACTION'
|
||||||
export const UNKNOWN_CMD = 'FLOWS_UNKNOWN_CMD'
|
|
||||||
export const FETCH_ERROR = 'FLOWS_FETCH_ERROR'
|
|
||||||
export const SELECT = 'FLOWS_SELECT'
|
|
||||||
|
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
|
highlight: null,
|
||||||
|
filter: null,
|
||||||
|
sort: { column: null, desc: false },
|
||||||
selected: [],
|
selected: [],
|
||||||
...reduceList(undefined, {}),
|
...reduceStore(undefined, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function reduce(state = defaultState, action) {
|
export default function reduce(state = defaultState, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case ADD:
|
case ADD:
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...reduceList(state, listActions.add(action.item)),
|
|
||||||
}
|
|
||||||
|
|
||||||
case UPDATE:
|
case UPDATE:
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...reduceList(state, listActions.update(action.item)),
|
|
||||||
}
|
|
||||||
|
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
|
case RECEIVE:
|
||||||
|
// FIXME: Implement select switch for remove
|
||||||
|
let storeAction = storeActions[action.cmd](
|
||||||
|
action.data,
|
||||||
|
makeFilter(state.filter),
|
||||||
|
makeSort(state.sort)
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...reduceList(state, listActions.remove(action.id)),
|
...reduceStore(state, storeAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
case RECEIVE:
|
case SET_FILTER:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...reduceList(state, listActions.receive(action.flows)),
|
filter: action.filter,
|
||||||
|
...reduceStore(state, storeActions.setFilter(makeFilter(action.filter), makeSort(state.sort)))
|
||||||
|
}
|
||||||
|
|
||||||
|
case SET_SORT:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
sort: action.sort,
|
||||||
|
...reduceStore(state, storeActions.setSort(makeSort(action.sort)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case SELECT:
|
case SELECT:
|
||||||
@ -51,13 +62,88 @@ export default function reduce(state = defaultState, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return {
|
return state
|
||||||
...state,
|
|
||||||
...reduceList(state, action),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const sortKeyFuns = {
|
||||||
|
|
||||||
|
TLSColumn: flow => flow.request.scheme,
|
||||||
|
|
||||||
|
PathColumn: flow => RequestUtils.pretty_url(flow.request),
|
||||||
|
|
||||||
|
MethodColumn: flow => flow.request.method,
|
||||||
|
|
||||||
|
StatusColumn: flow => flow.response && flow.response.status_code,
|
||||||
|
|
||||||
|
TimeColumn: flow => flow.response && flow.response.timestamp_end - flow.request.timestamp_start,
|
||||||
|
|
||||||
|
SizeColumn: flow => {
|
||||||
|
let total = flow.request.contentLength
|
||||||
|
if (flow.response) {
|
||||||
|
total += flow.response.contentLength || 0
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setFilter(filter) {
|
||||||
|
return { type: SET_FILTER, filter }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setHighlight(highlight) {
|
||||||
|
return { type: SET_HIGHLIGHT, highlight }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSort(column, desc) {
|
||||||
|
return { type: SET_SORT, sort: { column, desc } }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectRelative(shift) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
let currentSelectionIndex = getState().flows.viewIndex[getState().flows.selected[0]]
|
||||||
|
let minIndex = 0
|
||||||
|
let maxIndex = getState().flows.view.length - 1
|
||||||
|
let newIndex
|
||||||
|
if (currentSelectionIndex === undefined) {
|
||||||
|
newIndex = (shift < 0) ? minIndex : maxIndex
|
||||||
|
} else {
|
||||||
|
newIndex = currentSelectionIndex + shift
|
||||||
|
newIndex = window.Math.max(newIndex, minIndex)
|
||||||
|
newIndex = window.Math.min(newIndex, maxIndex)
|
||||||
|
}
|
||||||
|
let flow = getState().flows.view[newIndex]
|
||||||
|
dispatch(select(flow ? flow.id : undefined))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function accept(flow) {
|
export function accept(flow) {
|
||||||
return dispatch => fetchApi(`/flows/${flow.id}/accept`, { method: 'POST' })
|
return dispatch => fetchApi(`/flows/${flow.id}/accept`, { method: 'POST' })
|
||||||
}
|
}
|
||||||
@ -88,9 +174,9 @@ export function update(flow, data) {
|
|||||||
|
|
||||||
export function uploadContent(flow, file, type) {
|
export function uploadContent(flow, file, type) {
|
||||||
const body = new FormData()
|
const body = new FormData()
|
||||||
file = new Blob([file], {type: 'plain/text'})
|
file = new window.Blob([file], { type: 'plain/text' })
|
||||||
body.append('file', file)
|
body.append('file', file)
|
||||||
return dispatch => fetchApi(`/flows/${flow.id}/${type}/content`, {method: 'post', body} )
|
return dispatch => fetchApi(`/flows/${flow.id}/${type}/content`, { method: 'post', body })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,18 +1,12 @@
|
|||||||
import { combineReducers } from 'redux'
|
import { combineReducers } from 'redux'
|
||||||
import eventLog from './eventLog'
|
import eventLog from './eventLog'
|
||||||
import websocket from './websocket'
|
|
||||||
import flows from './flows'
|
import flows from './flows'
|
||||||
import flowView from './flowView'
|
|
||||||
import settings from './settings'
|
import settings from './settings'
|
||||||
import ui from './ui/index'
|
import ui from './ui/index'
|
||||||
import msgQueue from './msgQueue'
|
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
eventLog,
|
eventLog,
|
||||||
websocket,
|
|
||||||
flows,
|
flows,
|
||||||
flowView,
|
|
||||||
settings,
|
settings,
|
||||||
ui,
|
ui,
|
||||||
msgQueue,
|
|
||||||
})
|
})
|
||||||
|
@ -13,7 +13,7 @@ export default function reducer(state = defaultState, action) {
|
|||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case RECEIVE:
|
case RECEIVE:
|
||||||
return action.settings
|
return action.data
|
||||||
|
|
||||||
case UPDATE:
|
case UPDATE:
|
||||||
return {
|
return {
|
||||||
|
@ -60,7 +60,7 @@ export default function reducer(state = defaultState, action) {
|
|||||||
// There is no explicit "stop edit" event.
|
// There is no explicit "stop edit" event.
|
||||||
// We stop editing when we receive an update for
|
// We stop editing when we receive an update for
|
||||||
// the currently edited flow from the server
|
// the currently edited flow from the server
|
||||||
if (action.item.id === state.modifiedFlow.id) {
|
if (action.data.id === state.modifiedFlow.id) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
modifiedFlow: false,
|
modifiedFlow: false,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Key } from '../../utils'
|
import { Key } from '../../utils'
|
||||||
import { selectRelative as selectFlowRelative } from '../flowView'
|
|
||||||
import { selectTab } from './flow'
|
import { selectTab } from './flow'
|
||||||
import * as flowsActions from '../flows'
|
import * as flowsActions from '../flows'
|
||||||
|
|
||||||
@ -20,29 +19,29 @@ export function onKeyDown(e) {
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
case Key.K:
|
case Key.K:
|
||||||
case Key.UP:
|
case Key.UP:
|
||||||
dispatch(selectFlowRelative(-1))
|
dispatch(flowsActions.selectRelative(-1))
|
||||||
break
|
break
|
||||||
|
|
||||||
case Key.J:
|
case Key.J:
|
||||||
case Key.DOWN:
|
case Key.DOWN:
|
||||||
dispatch(selectFlowRelative(+1))
|
dispatch(flowsActions.selectRelative(+1))
|
||||||
break
|
break
|
||||||
|
|
||||||
case Key.SPACE:
|
case Key.SPACE:
|
||||||
case Key.PAGE_DOWN:
|
case Key.PAGE_DOWN:
|
||||||
dispatch(selectFlowRelative(+10))
|
dispatch(flowsActions.selectRelative(+10))
|
||||||
break
|
break
|
||||||
|
|
||||||
case Key.PAGE_UP:
|
case Key.PAGE_UP:
|
||||||
dispatch(selectFlowRelative(-10))
|
dispatch(flowsActions.selectRelative(-10))
|
||||||
break
|
break
|
||||||
|
|
||||||
case Key.END:
|
case Key.END:
|
||||||
dispatch(selectFlowRelative(+1e10))
|
dispatch(flowsActions.selectRelative(+1e10))
|
||||||
break
|
break
|
||||||
|
|
||||||
case Key.HOME:
|
case Key.HOME:
|
||||||
dispatch(selectFlowRelative(-1e10))
|
dispatch(flowsActions.selectRelative(-1e10))
|
||||||
break
|
break
|
||||||
|
|
||||||
case Key.ESC:
|
case Key.ESC:
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
export const ADD = 'LIST_ADD'
|
|
||||||
export const UPDATE = 'LIST_UPDATE'
|
|
||||||
export const REMOVE = 'LIST_REMOVE'
|
|
||||||
export const RECEIVE = 'LIST_RECEIVE'
|
|
||||||
|
|
||||||
const defaultState = {
|
|
||||||
data: [],
|
|
||||||
byId: {},
|
|
||||||
indexOf: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function reduce(state = defaultState, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case ADD:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data: [...state.data, action.item],
|
|
||||||
byId: { ...state.byId, [action.item.id]: action.item },
|
|
||||||
indexOf: { ...state.indexOf, [action.item.id]: state.data.length },
|
|
||||||
}
|
|
||||||
|
|
||||||
case UPDATE: {
|
|
||||||
const index = state.indexOf[action.item.id]
|
|
||||||
|
|
||||||
if (index == null) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = [...state.data]
|
|
||||||
|
|
||||||
data[index] = action.item
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data,
|
|
||||||
byId: { ...state.byId, [action.item.id]: action.item }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case REMOVE: {
|
|
||||||
const index = state.indexOf[action.id]
|
|
||||||
|
|
||||||
if (index == null) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = [...state.data]
|
|
||||||
const indexOf = { ...state.indexOf, [action.id]: null }
|
|
||||||
|
|
||||||
data.splice(index, 1)
|
|
||||||
for (let i = data.length - 1; i >= index; i--) {
|
|
||||||
indexOf[data[i].id] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data,
|
|
||||||
indexOf,
|
|
||||||
byId: { ...state.byId, [action.id]: null },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case RECEIVE:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data: action.list,
|
|
||||||
byId: _.fromPairs(action.list.map(item => [item.id, item])),
|
|
||||||
indexOf: _.fromPairs(action.list.map((item, index) => [item.id, index])),
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function add(item) {
|
|
||||||
return { type: ADD, item }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function update(item) {
|
|
||||||
return { type: UPDATE, item }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function remove(id) {
|
|
||||||
return { type: REMOVE, id }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function receive(list) {
|
|
||||||
return { type: RECEIVE, list }
|
|
||||||
}
|
|
194
web/src/js/ducks/utils/store.js
Normal file
194
web/src/js/ducks/utils/store.js
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
export const SET_FILTER = 'LIST_SET_FILTER'
|
||||||
|
export const SET_SORT = 'LIST_SET_SORT'
|
||||||
|
export const ADD = 'LIST_ADD'
|
||||||
|
export const UPDATE = 'LIST_UPDATE'
|
||||||
|
export const REMOVE = 'LIST_REMOVE'
|
||||||
|
export const RECEIVE = 'LIST_RECEIVE'
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
byId: {},
|
||||||
|
list: [],
|
||||||
|
listIndex: {},
|
||||||
|
view: [],
|
||||||
|
viewIndex: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function reduce(state = defaultState, action) {
|
||||||
|
|
||||||
|
let { byId, list, listIndex, view, viewIndex } = state
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case SET_FILTER:
|
||||||
|
view = list.filter(action.filter).sort(action.sort)
|
||||||
|
viewIndex = {}
|
||||||
|
view.forEach((item, index) => {
|
||||||
|
viewIndex[item.id] = index
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
case SET_SORT:
|
||||||
|
view = [...view].sort(action.sort)
|
||||||
|
viewIndex = {}
|
||||||
|
view.forEach((item, index) => {
|
||||||
|
viewIndex[item.id] = index
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
case ADD:
|
||||||
|
if (action.item.id in byId) {
|
||||||
|
// we already had that.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
byId = { ...byId, [action.item.id]: action.item }
|
||||||
|
listIndex = { ...listIndex, [action.item.id]: list.length }
|
||||||
|
list = [...list, action.item]
|
||||||
|
if (action.filter(action.item)) {
|
||||||
|
({ view, viewIndex } = sortedInsert(state, action.item, action.sort))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case UPDATE:
|
||||||
|
byId = { ...byId, [action.item.id]: action.item }
|
||||||
|
list = [...list]
|
||||||
|
list[listIndex[action.item.id]] = action.item
|
||||||
|
|
||||||
|
let hasOldItem = action.item.id in viewIndex
|
||||||
|
let hasNewItem = action.filter(action.item)
|
||||||
|
if (hasNewItem && !hasOldItem) {
|
||||||
|
({view, viewIndex} = sortedInsert(state, action.item, action.sort))
|
||||||
|
}
|
||||||
|
else if (!hasNewItem && hasOldItem) {
|
||||||
|
({data: view, dataIndex: viewIndex} = removeData(view, viewIndex, action.item.id))
|
||||||
|
}
|
||||||
|
else if (hasNewItem && hasOldItem) {
|
||||||
|
({view, viewIndex} = sortedUpdate(state, action.item, action.sort))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case REMOVE:
|
||||||
|
if (!(action.id in byId)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
delete byId[action.id];
|
||||||
|
({data: list, dataIndex: listIndex} = removeData(list, listIndex, action.id))
|
||||||
|
|
||||||
|
if (action.id in viewIndex) {
|
||||||
|
({data: view, dataIndex: viewIndex} = removeData(view, viewIndex, action.id))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case RECEIVE:
|
||||||
|
list = action.list
|
||||||
|
listIndex = {}
|
||||||
|
byId = {}
|
||||||
|
list.forEach((item, i) => {
|
||||||
|
byId[item.id] = item
|
||||||
|
listIndex[item.id] = i
|
||||||
|
})
|
||||||
|
view = list.filter(action.filter).sort(action.sort)
|
||||||
|
viewIndex = {}
|
||||||
|
view.forEach((item, index) => {
|
||||||
|
viewIndex[item.id] = index
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return { byId, list, listIndex, view, viewIndex }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function setFilter(filter = defaultFilter, sort = defaultSort) {
|
||||||
|
return { type: SET_FILTER, filter, sort }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSort(sort = defaultSort) {
|
||||||
|
return { type: SET_SORT, sort }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function add(item, filter = defaultFilter, sort = defaultSort) {
|
||||||
|
return { type: ADD, item, filter, sort }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update(item, filter = defaultFilter, sort = defaultSort) {
|
||||||
|
return { type: UPDATE, item, filter, sort }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function remove(id) {
|
||||||
|
return { type: REMOVE, id }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function receive(list, filter = defaultFilter, sort = defaultSort) {
|
||||||
|
return { type: RECEIVE, list, filter, sort }
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortedInsert(state, item, sort) {
|
||||||
|
const index = sortedIndex(state.view, item, sort)
|
||||||
|
const view = [...state.view]
|
||||||
|
const viewIndex = { ...state.viewIndex }
|
||||||
|
|
||||||
|
view.splice(index, 0, item)
|
||||||
|
for (let i = view.length - 1; i >= index; i--) {
|
||||||
|
viewIndex[view[i].id] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
return { view, viewIndex }
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeData(currentData, currentDataIndex, id) {
|
||||||
|
const index = currentDataIndex[id]
|
||||||
|
const data = [...currentData]
|
||||||
|
const dataIndex = { ...currentDataIndex }
|
||||||
|
delete dataIndex[id];
|
||||||
|
|
||||||
|
data.splice(index, 1)
|
||||||
|
for (let i = data.length - 1; i >= index; i--) {
|
||||||
|
dataIndex[data[i].id] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data, dataIndex }
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortedUpdate(state, item, sort) {
|
||||||
|
let view = [...state.view]
|
||||||
|
let viewIndex = { ...state.viewIndex }
|
||||||
|
let index = viewIndex[item.id]
|
||||||
|
view[index] = item
|
||||||
|
while (index + 1 < view.length && sort(view[index], view[index + 1]) > 0) {
|
||||||
|
view[index] = view[index + 1]
|
||||||
|
view[index + 1] = item
|
||||||
|
viewIndex[item.id] = index + 1
|
||||||
|
viewIndex[view[index].id] = index
|
||||||
|
++index
|
||||||
|
}
|
||||||
|
while (index > 0 && sort(view[index], view[index - 1]) < 0) {
|
||||||
|
view[index] = view[index - 1]
|
||||||
|
view[index - 1] = item
|
||||||
|
viewIndex[item.id] = index - 1
|
||||||
|
viewIndex[view[index].id] = index
|
||||||
|
--index
|
||||||
|
}
|
||||||
|
return { view, viewIndex }
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortedIndex(list, item, sort) {
|
||||||
|
let low = 0
|
||||||
|
let high = list.length
|
||||||
|
|
||||||
|
while (low < high) {
|
||||||
|
const middle = (low + high) >>> 1
|
||||||
|
if (sort(item, list[middle]) >= 0) {
|
||||||
|
low = middle + 1
|
||||||
|
} else {
|
||||||
|
high = middle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return low
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultFilter() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultSort(a, b) {
|
||||||
|
return 0
|
||||||
|
}
|
@ -1,189 +0,0 @@
|
|||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
export const UPDATE_FILTER = 'VIEW_UPDATE_FILTER'
|
|
||||||
export const UPDATE_SORT = 'VIEW_UPDATE_SORT'
|
|
||||||
export const ADD = 'VIEW_ADD'
|
|
||||||
export const UPDATE = 'VIEW_UPDATE'
|
|
||||||
export const REMOVE = 'VIEW_REMOVE'
|
|
||||||
export const RECEIVE = 'VIEW_RECEIVE'
|
|
||||||
|
|
||||||
const defaultState = {
|
|
||||||
data: [],
|
|
||||||
indexOf: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function reduce(state = defaultState, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case UPDATE_FILTER:
|
|
||||||
{
|
|
||||||
const data = action.list.filter(action.filter).sort(action.sort)
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data,
|
|
||||||
indexOf: _.fromPairs(data.map((item, index) => [item.id, index])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case UPDATE_SORT:
|
|
||||||
{
|
|
||||||
const data = [...state.data].sort(action.sort)
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data,
|
|
||||||
indexOf: _.fromPairs(data.map((item, index) => [item.id, index])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ADD:
|
|
||||||
if (state.indexOf[action.item.id] != null || !action.filter(action.item)) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...sortedInsert(state, action.item, action.sort),
|
|
||||||
}
|
|
||||||
|
|
||||||
case REMOVE:
|
|
||||||
if (state.indexOf[action.id] == null) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...sortedRemove(state, action.id),
|
|
||||||
}
|
|
||||||
|
|
||||||
case UPDATE:
|
|
||||||
let hasOldItem = state.indexOf[action.item.id] !== null && state.indexOf[action.item.id] !== undefined
|
|
||||||
let hasNewItem = action.filter(action.item)
|
|
||||||
if (!hasNewItem && !hasOldItem) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
if (hasNewItem && !hasOldItem) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...sortedInsert(state, action.item, action.sort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasNewItem && hasOldItem) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...sortedRemove(state, action.item.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasNewItem && hasOldItem) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...sortedUpdate(state, action.item, action.sort),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case RECEIVE:
|
|
||||||
{
|
|
||||||
const data = action.list.filter(action.filter).sort(action.sort)
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data,
|
|
||||||
indexOf: _.fromPairs(data.map((item, index) => [item.id, index])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateFilter(list, filter = defaultFilter, sort = defaultSort) {
|
|
||||||
return { type: UPDATE_FILTER, list, filter, sort }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSort(sort = defaultSort) {
|
|
||||||
return { type: UPDATE_SORT, sort }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function add(item, filter = defaultFilter, sort = defaultSort) {
|
|
||||||
return { type: ADD, item, filter, sort }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function update(item, filter = defaultFilter, sort = defaultSort) {
|
|
||||||
return { type: UPDATE, item, filter, sort }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function remove(id) {
|
|
||||||
return { type: REMOVE, id }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function receive(list, filter = defaultFilter, sort = defaultSort) {
|
|
||||||
return { type: RECEIVE, list, filter, sort }
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortedInsert(state, item, sort) {
|
|
||||||
const index = sortedIndex(state.data, item, sort)
|
|
||||||
const data = [ ...state.data ]
|
|
||||||
const indexOf = { ...state.indexOf }
|
|
||||||
|
|
||||||
data.splice(index, 0, item)
|
|
||||||
for (let i = data.length - 1; i >= index; i--) {
|
|
||||||
indexOf[data[i].id] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
return { data, indexOf }
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortedRemove(state, id) {
|
|
||||||
const index = state.indexOf[id]
|
|
||||||
const data = [...state.data]
|
|
||||||
const indexOf = { ...state.indexOf, [id]: null }
|
|
||||||
|
|
||||||
data.splice(index, 1)
|
|
||||||
for (let i = data.length - 1; i >= index; i--) {
|
|
||||||
indexOf[data[i].id] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
return { data, indexOf }
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortedUpdate(state, item, sort) {
|
|
||||||
let data = [ ...state.data ]
|
|
||||||
let indexOf = { ...state.indexOf }
|
|
||||||
let index = indexOf[item.id]
|
|
||||||
data[index] = item
|
|
||||||
while (index + 1 < data.length && sort(data[index], data[index + 1]) > 0) {
|
|
||||||
data[index] = data[index + 1]
|
|
||||||
data[index + 1] = item
|
|
||||||
indexOf[item.id] = index + 1
|
|
||||||
indexOf[data[index].id] = index
|
|
||||||
++index
|
|
||||||
}
|
|
||||||
while (index > 0 && sort(data[index], data[index - 1]) < 0) {
|
|
||||||
data[index] = data[index - 1]
|
|
||||||
data[index - 1] = item
|
|
||||||
indexOf[item.id] = index - 1
|
|
||||||
indexOf[data[index].id] = index
|
|
||||||
--index
|
|
||||||
}
|
|
||||||
return { data, indexOf }
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortedIndex(list, item, sort) {
|
|
||||||
let low = 0
|
|
||||||
let high = list.length
|
|
||||||
|
|
||||||
while (low < high) {
|
|
||||||
const middle = (low + high) >>> 1
|
|
||||||
if (sort(item, list[middle]) >= 0) {
|
|
||||||
low = middle + 1
|
|
||||||
} else {
|
|
||||||
high = middle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return low
|
|
||||||
}
|
|
||||||
|
|
||||||
function defaultFilter() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function defaultSort(a, b) {
|
|
||||||
return 0
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import { select } from "./ducks/flows"
|
import { select, setFilter, setHighlight } from "./ducks/flows"
|
||||||
import { selectTab } from "./ducks/ui/flow"
|
import { selectTab } from "./ducks/ui/flow"
|
||||||
import { updateFilter, updateHighlight } from "./ducks/flowView"
|
|
||||||
import { toggleVisibility } from "./ducks/eventLog"
|
import { toggleVisibility } from "./ducks/eventLog"
|
||||||
|
|
||||||
const Query = {
|
const Query = {
|
||||||
@ -10,7 +9,7 @@ const Query = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function updateStoreFromUrl(store) {
|
function updateStoreFromUrl(store) {
|
||||||
const [path, query] = window.location.hash.substr(1).split("?", 2)
|
const [path, query] = window.location.hash.substr(1).split("?", 2)
|
||||||
const path_components = path.substr(1).split("/")
|
const path_components = path.substr(1).split("/")
|
||||||
|
|
||||||
if (path_components[0] === "flows") {
|
if (path_components[0] === "flows") {
|
||||||
@ -28,14 +27,14 @@ function updateStoreFromUrl(store) {
|
|||||||
const [key, value] = x.split("=", 2)
|
const [key, value] = x.split("=", 2)
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case Query.SEARCH:
|
case Query.SEARCH:
|
||||||
store.dispatch(updateFilter(value))
|
store.dispatch(setFilter(value))
|
||||||
break
|
break
|
||||||
case Query.HIGHLIGHT:
|
case Query.HIGHLIGHT:
|
||||||
store.dispatch(updateHighlight(value))
|
store.dispatch(setHighlight(value))
|
||||||
break
|
break
|
||||||
case Query.SHOW_EVENTLOG:
|
case Query.SHOW_EVENTLOG:
|
||||||
if(!store.getState().eventLog.visible)
|
if (!store.getState().eventLog.visible)
|
||||||
store.dispatch(toggleVisibility(value))
|
store.dispatch(toggleVisibility())
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.error(`unimplemented query arg: ${x}`)
|
console.error(`unimplemented query arg: ${x}`)
|
||||||
@ -45,10 +44,10 @@ function updateStoreFromUrl(store) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateUrlFromStore(store) {
|
function updateUrlFromStore(store) {
|
||||||
const state = store.getState()
|
const state = store.getState()
|
||||||
let query = {
|
let query = {
|
||||||
[Query.SEARCH]: state.flowView.filter,
|
[Query.SEARCH]: state.flows.filter,
|
||||||
[Query.HIGHLIGHT]: state.flowView.highlight,
|
[Query.HIGHLIGHT]: state.flows.highlight,
|
||||||
[Query.SHOW_EVENTLOG]: state.eventLog.visible,
|
[Query.SHOW_EVENTLOG]: state.eventLog.visible,
|
||||||
}
|
}
|
||||||
const queryStr = Object.keys(query)
|
const queryStr = Object.keys(query)
|
||||||
|
Loading…
Reference in New Issue
Block a user