From 29997bca4b6436e5837c7d086d3311d6e79e9ca4 Mon Sep 17 00:00:00 2001 From: "zokutyou2@gmail.com" Date: Mon, 19 Jul 2021 15:47:30 +0900 Subject: [PATCH] convert components in FlowView, Header, Modal, ValueEditor into typescript --- .../components/Header/FlowMenuSpec.js | 84 +------- .../Header/__snapshots__/FlowMenuSpec.js.snap | 118 ---------- web/src/js/__tests__/ducks/tutils.ts | 2 + .../js/components/ContentView/CodeEditor.tsx | 9 +- .../components/ContentView/ContentLoader.tsx | 10 +- .../ContentView/ContentViewOptions.tsx | 7 +- .../components/ContentView/ContentViews.tsx | 4 +- .../ContentView/DownloadContentButton.tsx | 5 +- .../js/components/ContentView/MetaViews.tsx | 5 +- .../ContentView/ShowFullContentButton.tsx | 3 +- .../ContentView/UploadContentButton.tsx | 2 +- .../components/{FlowView.jsx => FlowView.tsx} | 22 +- .../FlowView/{Details.jsx => Details.tsx} | 23 +- web/src/js/components/FlowView/Messages.jsx | 203 ------------------ web/src/js/components/FlowView/Messages.tsx | 198 +++++++++++++++++ .../components/FlowView/{Nav.jsx => Nav.tsx} | 22 +- web/src/js/components/FlowView/ToggleEdit.jsx | 35 +-- web/src/js/components/Header.jsx | 1 - .../Header/{FilterDocs.jsx => FilterDocs.tsx} | 17 +- .../Header/{FlowMenu.jsx => FlowMenu.tsx} | 51 ++--- .../Header/{OptionMenu.jsx => OptionMenu.tsx} | 16 +- .../components/{MainView.jsx => MainView.tsx} | 17 +- web/src/js/components/Modal/Modal.jsx | 27 +-- .../Modal/{ModalList.jsx => ModalList.tsx} | 0 web/src/js/components/ProxyApp.jsx | 47 ---- web/src/js/components/ProxyApp.tsx | 35 +++ ...{ValidateEditor.jsx => ValidateEditor.tsx} | 21 +- web/src/js/components/common/DocsLink.tsx | 2 +- web/src/js/components/common/HideInStatic.tsx | 2 +- web/src/js/components/common/ToggleButton.tsx | 2 +- .../components/common/ToggleInputButton.tsx | 2 +- web/src/js/ducks/ui/{header.js => header.ts} | 0 web/src/js/ducks/ui/{modal.js => modal.ts} | 0 .../ui/{optionsEditor.js => optionsEditor.ts} | 0 web/src/js/flow.ts | 1 + web/src/js/utils.ts | 2 +- 36 files changed, 380 insertions(+), 615 deletions(-) rename web/src/js/components/{FlowView.jsx => FlowView.tsx} (71%) rename web/src/js/components/FlowView/{Details.jsx => Details.tsx} (90%) delete mode 100644 web/src/js/components/FlowView/Messages.jsx create mode 100644 web/src/js/components/FlowView/Messages.tsx rename web/src/js/components/FlowView/{Nav.jsx => Nav.tsx} (64%) rename web/src/js/components/Header/{FilterDocs.jsx => FilterDocs.tsx} (80%) rename web/src/js/components/Header/{FlowMenu.jsx => FlowMenu.tsx} (66%) rename web/src/js/components/Header/{OptionMenu.jsx => OptionMenu.tsx} (86%) rename web/src/js/components/{MainView.jsx => MainView.tsx} (52%) rename web/src/js/components/Modal/{ModalList.jsx => ModalList.tsx} (100%) delete mode 100644 web/src/js/components/ProxyApp.jsx create mode 100644 web/src/js/components/ProxyApp.tsx rename web/src/js/components/ValueEditor/{ValidateEditor.jsx => ValidateEditor.tsx} (78%) mode change 100755 => 100644 rename web/src/js/ducks/ui/{header.js => header.ts} (100%) rename web/src/js/ducks/ui/{modal.js => modal.ts} (100%) rename web/src/js/ducks/ui/{optionsEditor.js => optionsEditor.ts} (100%) diff --git a/web/src/js/__tests__/components/Header/FlowMenuSpec.js b/web/src/js/__tests__/components/Header/FlowMenuSpec.js index 65fde2136..6ca7913d6 100644 --- a/web/src/js/__tests__/components/Header/FlowMenuSpec.js +++ b/web/src/js/__tests__/components/Header/FlowMenuSpec.js @@ -2,75 +2,29 @@ jest.mock('../../../flow/utils') import React from 'react' import renderer from 'react-test-renderer' -import ConnectedFlowMenu, { FlowMenu } from '../../../components/Header/FlowMenu' +import FlowMenu from '../../../components/Header/FlowMenu' import { TFlow, TStore }from '../../ducks/tutils' import { MessageUtils } from "../../../flow/utils" import { Provider } from 'react-redux' describe('FlowMenu Component', () => { - let actions = { - resumeFlow: jest.fn(), - killFlow: jest.fn(), - replayFlow: jest.fn(), - duplicateFlow: jest.fn(), - removeFlow: jest.fn(), - revertFlow: jest.fn() - }, - tflow = new TFlow() + let tflow = new TFlow(), + store = new TStore() tflow.modified = true tflow.intercepted = true + global.fetch = jest.fn() - it('should render correctly without flow', () => { - let flowMenu = renderer.create( - ), - tree = flowMenu.toJSON() - expect(tree).toMatchSnapshot() - }) - - let flowMenu = renderer.create(), + let flowMenu = renderer.create( + + + + ), tree = flowMenu.toJSON() it('should render correctly with flow', () => { expect(tree).toMatchSnapshot() }) - let menu_content_1 = tree.children[0].children[0] - it('should handle replayFlow', () => { - let button = menu_content_1.children[0] - button.props.onClick() - expect(actions.replayFlow).toBeCalledWith(tflow) - }) - - it('should handle duplicateFlow', () => { - let button = menu_content_1.children[1] - button.props.onClick() - expect(actions.duplicateFlow).toBeCalledWith(tflow) - }) - - it('should handle revertFlow', () => { - let button = menu_content_1.children[2] - button.props.onClick() - expect(actions.revertFlow).toBeCalledWith(tflow) - }) - - it('should handle removeFlow', () => { - let button = menu_content_1.children[3] - button.props.onClick() - expect(actions.removeFlow).toBeCalledWith(tflow) - }) - let menu_content_2 = tree.children[1].children[0] it('should handle download', () => { let button = menu_content_2.children[0] @@ -78,24 +32,4 @@ describe('FlowMenu Component', () => { expect(MessageUtils.getContentURL).toBeCalledWith(tflow, tflow.response) }) - let menu_content_3 = tree.children[2].children[0] - it('should handle resumeFlow', () => { - let button = menu_content_3.children[0] - button.props.onClick() - expect(actions.resumeFlow).toBeCalledWith(tflow) - }) - - it('should handle killFlow', () => { - let button = menu_content_3.children[1] - button.props.onClick() - expect(actions.killFlow).toBeCalledWith(tflow) - }) - - it('should connect to state', () => { - let store = TStore(), - provider = renderer.create(), - tree = provider.toJSON() - expect(tree).toMatchSnapshot() - }) - }) diff --git a/web/src/js/__tests__/components/Header/__snapshots__/FlowMenuSpec.js.snap b/web/src/js/__tests__/components/Header/__snapshots__/FlowMenuSpec.js.snap index e4d16d13e..6ccc1251b 100644 --- a/web/src/js/__tests__/components/Header/__snapshots__/FlowMenuSpec.js.snap +++ b/web/src/js/__tests__/components/Header/__snapshots__/FlowMenuSpec.js.snap @@ -1,121 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`FlowMenu Component should connect to state 1`] = ` -
-
-
- - - - -
-
- Flow Modification -
-
-
-
- -
-
- Export -
-
-
-
- - -
-
- Interception -
-
-
-`; - exports[`FlowMenu Component should render correctly with flow 1`] = `
`; - -exports[`FlowMenu Component should render correctly without flow 1`] = `
`; diff --git a/web/src/js/__tests__/ducks/tutils.ts b/web/src/js/__tests__/ducks/tutils.ts index c447f1346..2780bd052 100644 --- a/web/src/js/__tests__/ducks/tutils.ts +++ b/web/src/js/__tests__/ducks/tutils.ts @@ -21,6 +21,8 @@ export {TFlow} const tflow1: HTTPFlow = TFlow(); const tflow2: HTTPFlow = TFlow(); +tflow1.modified = true +tflow1.intercepted = true tflow2.id = "flow2"; tflow2.request.path = "/second"; diff --git a/web/src/js/components/ContentView/CodeEditor.tsx b/web/src/js/components/ContentView/CodeEditor.tsx index 6944e70fc..063e42629 100644 --- a/web/src/js/components/ContentView/CodeEditor.tsx +++ b/web/src/js/components/ContentView/CodeEditor.tsx @@ -1,14 +1,13 @@ import React from 'react' -import PropTypes from 'prop-types' import CodeMirror from "../../contrib/CodeMirror" -CodeEditor.propTypes = { - content: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, +type CodeEditorProps = { + content: string, + onChange: Function, } -export default function CodeEditor ( { content, onChange} ){ +export default function CodeEditor ( { content, onChange}: CodeEditorProps ){ let options = { lineNumbers: true diff --git a/web/src/js/components/ContentView/ContentLoader.tsx b/web/src/js/components/ContentView/ContentLoader.tsx index 4d6ce63a4..9c1aa824a 100644 --- a/web/src/js/components/ContentView/ContentLoader.tsx +++ b/web/src/js/components/ContentView/ContentLoader.tsx @@ -1,14 +1,12 @@ -import React, { Component } from 'react' +import React from 'react' import { MessageUtils } from '../../flow/utils' +import { Flow, HTTPMessage } from '../../flow' type ContentLoaderProps = { content: string, contentView: object, - flow: object, - message: { - content: string, - contentHash: string, - }, + flow: Flow, + message: HTTPMessage, } type ContentLoaderStates = { diff --git a/web/src/js/components/ContentView/ContentViewOptions.tsx b/web/src/js/components/ContentView/ContentViewOptions.tsx index 8a486833f..2fcb8186d 100644 --- a/web/src/js/components/ContentView/ContentViewOptions.tsx +++ b/web/src/js/components/ContentView/ContentViewOptions.tsx @@ -3,11 +3,12 @@ import ViewSelector from './ViewSelector' import UploadContentButton from './UploadContentButton' import DownloadContentButton from './DownloadContentButton' import { useAppSelector } from "../../ducks"; +import { Flow, HTTPMessage } from '../../flow' type ContentViewOptionsProps = { - flow: object, - message: object, - uploadContent: () => void, + flow: Flow, + message: HTTPMessage, + uploadContent: (content: string) => Promise, } export default function ContentViewOptions({ flow, message, uploadContent }: ContentViewOptionsProps) { diff --git a/web/src/js/components/ContentView/ContentViews.tsx b/web/src/js/components/ContentView/ContentViews.tsx index 5a521240a..fb2982d83 100644 --- a/web/src/js/components/ContentView/ContentViews.tsx +++ b/web/src/js/components/ContentView/ContentViews.tsx @@ -24,7 +24,7 @@ function ViewImage({ flow, message }: ViewImageProps) { type EditProps = { content: string, - onChange: () => void, + onChange: (content: string) => any, } function PureEdit({ content, onChange }: EditProps) { @@ -39,7 +39,7 @@ type PureViewServerProps = { } type PureViewServerStates = { - lines: string[][], + lines: [style: string, text: string][][], description: string, } diff --git a/web/src/js/components/ContentView/DownloadContentButton.tsx b/web/src/js/components/ContentView/DownloadContentButton.tsx index b538038d5..2970ea1f7 100644 --- a/web/src/js/components/ContentView/DownloadContentButton.tsx +++ b/web/src/js/components/ContentView/DownloadContentButton.tsx @@ -1,9 +1,10 @@ import React from 'react' import { MessageUtils } from "../../flow/utils" +import { Flow, HTTPMessage } from '../../flow' type DownloadContentButtonProps = { - flow: object, - message: object, + flow: Flow, + message: HTTPMessage, } export default function DownloadContentButton({ flow, message }: DownloadContentButtonProps) { diff --git a/web/src/js/components/ContentView/MetaViews.tsx b/web/src/js/components/ContentView/MetaViews.tsx index bdf5c2c1b..1dc7235d4 100644 --- a/web/src/js/components/ContentView/MetaViews.tsx +++ b/web/src/js/components/ContentView/MetaViews.tsx @@ -2,10 +2,11 @@ import React from 'react' import { formatSize } from '../../utils' import UploadContentButton from './UploadContentButton' import DownloadContentButton from './DownloadContentButton' +import { HTTPFlow, HTTPMessage } from '../../flow' interface ContentProps { - flow: { request: object }, - message: { contentLength: number }, + flow: HTTPFlow, + message: HTTPMessage, } interface ContentTooLargeProps extends ContentProps { diff --git a/web/src/js/components/ContentView/ShowFullContentButton.tsx b/web/src/js/components/ContentView/ShowFullContentButton.tsx index 9c1b15b88..906bd3b01 100644 --- a/web/src/js/components/ContentView/ShowFullContentButton.tsx +++ b/web/src/js/components/ContentView/ShowFullContentButton.tsx @@ -10,13 +10,14 @@ export default function ShowFullContentButton() { contentLines = useAppSelector(state => state.ui.flow.content.length) return ( - !showFullContent && + !showFullContent ? (
{visibleLines}/{contentLines} are visible  
+ ) : null ) } diff --git a/web/src/js/components/ContentView/UploadContentButton.tsx b/web/src/js/components/ContentView/UploadContentButton.tsx index 9b7281409..666e155d6 100644 --- a/web/src/js/components/ContentView/UploadContentButton.tsx +++ b/web/src/js/components/ContentView/UploadContentButton.tsx @@ -2,7 +2,7 @@ import React from 'react' import FileChooser from '../common/FileChooser' type UploadContentButtonProps = { - uploadContent: () => any, + uploadContent: (content: string) => Promise, } export default function UploadContentButton({ uploadContent }: UploadContentButtonProps) { diff --git a/web/src/js/components/FlowView.jsx b/web/src/js/components/FlowView.tsx similarity index 71% rename from web/src/js/components/FlowView.jsx rename to web/src/js/components/FlowView.tsx index 25e7bb9fc..bdaad7c7d 100644 --- a/web/src/js/components/FlowView.jsx +++ b/web/src/js/components/FlowView.tsx @@ -1,5 +1,4 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' +import React from 'react' import _ from 'lodash' import Nav from './FlowView/Nav' @@ -7,10 +6,15 @@ import { ErrorView as Error, Request, Response } from './FlowView/Messages' import Details from './FlowView/Details' import { selectTab } from '../ducks/ui/flow' +import {useAppDispatch, useAppSelector} from "../ducks"; export const allTabs = { Request, Response, Error, Details } -function FlowView({ flow, tabName, selectTab }) { +export default function FlowView() { + const dispatch = useAppDispatch(), + flow = useAppSelector(state => state.flows.byId[state.flows.selected[0]]) + + let tabName = useAppSelector(state => state.ui.flow.tab) // only display available tab names const tabs = ['request', 'response', 'error'].filter(k => flow[k]) @@ -33,19 +37,9 @@ function FlowView({ flow, tabName, selectTab }) {
) } - -export default connect( - state => ({ - flow: state.flows.byId[state.flows.selected[0]], - tabName: state.ui.flow.tab, - }), - { - selectTab, - } -)(FlowView) diff --git a/web/src/js/components/FlowView/Details.jsx b/web/src/js/components/FlowView/Details.tsx similarity index 90% rename from web/src/js/components/FlowView/Details.jsx rename to web/src/js/components/FlowView/Details.tsx index e8a35cfac..3bec1c138 100644 --- a/web/src/js/components/FlowView/Details.jsx +++ b/web/src/js/components/FlowView/Details.tsx @@ -1,7 +1,14 @@ import React from 'react' import {formatTimeDelta, formatTimeStamp} from '../../utils' +import { Flow, HTTPMessage } from '../../flow' -export function TimeStamp({t, deltaTo, title}) { +type TimeStampProps = { + t: number, + deltaTo: number, + title: string, +} + +export function TimeStamp({t, deltaTo, title}: TimeStampProps) { return t ? ( {title}: @@ -19,7 +26,19 @@ export function TimeStamp({t, deltaTo, title}) { ) } -export function ConnectionInfo({conn}) { +type ConnectionInfoProps = { + conn: { + address: string[], + sni: string, + tls_version: string, + cipher_name: string, + alpn_proto_negotiated: string, + ip_address: string[], + source_address: string[], + }, +} + +export function ConnectionInfo({conn}: ConnectionInfoProps) { return ( diff --git a/web/src/js/components/FlowView/Messages.jsx b/web/src/js/components/FlowView/Messages.jsx deleted file mode 100644 index e71b65100..000000000 --- a/web/src/js/components/FlowView/Messages.jsx +++ /dev/null @@ -1,203 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - - -import { RequestUtils, isValidHttpVersion, parseUrl } from '../../flow/utils' -import { formatTimeStamp } from '../../utils' -import ContentView from '../ContentView' -import ContentViewOptions from '../ContentView/ContentViewOptions' -import ValidateEditor from '../ValueEditor/ValidateEditor' -import ValueEditor from '../ValueEditor/ValueEditor' -import HideInStatic from '../common/HideInStatic' - -import Headers from './Headers' -import { startEdit, updateEdit } from '../../ducks/ui/flow' -import * as FlowActions from '../../ducks/flows' -import ToggleEdit from './ToggleEdit' - -function RequestLine({ flow, readonly, updateFlow }) { - return ( -
-
- updateFlow({ request: { method } })} - /> -   - updateFlow({ request: {path: '', ...parseUrl(url)}})} - isValid={url => !!parseUrl(url).host} - /> -   - updateFlow({ request: { http_version } })} - isValid={isValidHttpVersion} - /> -
-
- ) -} - -function ResponseLine({ flow, readonly, updateFlow }) { - return ( -
- updateFlow({ response: { http_version: nextVer } })} - isValid={isValidHttpVersion} - /> -   - updateFlow({ response: { code: parseInt(code) } })} - isValid={code => /^\d+$/.test(code)} - /> -   - updateFlow({ response: { msg } })} - /> -
- ) -} - -const Message = connect( - state => ({ - flow: state.ui.flow.modifiedFlow || state.flows.byId[state.flows.selected[0]], - isEdit: !!state.ui.flow.modifiedFlow, - }), - { - updateFlow: updateEdit, - uploadContent: FlowActions.uploadContent - } -) - -export class Request extends Component { - render() { - const { flow, isEdit, updateFlow, uploadContent } = this.props - let noContent = !isEdit && (flow.request.contentLength == 0 || flow.request.contentLength == null) - return ( -
-
- - - updateFlow({ request: { headers } })} - /> - -
- updateFlow({ request: {content}})} - message={flow.request}/> - -
- updateFlow({ request: { trailers } })} - type='trailers' - /> -
- - {!noContent && -
- uploadContent(flow, content, "request")}/> -
- } -
-
- ) - } -} - -Request = Message(Request) - - -export class Response extends Component { - render() { - const { flow, isEdit, updateFlow, uploadContent } = this.props - let noContent = !isEdit && (flow.response.contentLength == 0 || flow.response.contentLength == null) - - return ( -
-
- - - updateFlow({ response: { headers } })} - /> -
- updateFlow({ response: {content}})} - message={flow.response} - /> -
- updateFlow({ response: { trailers } })} - type='trailers' - /> -
- - {!noContent && -
- uploadContent(flow, content, "response")} - readonly={!isEdit}/> -
- } -
-
- ) - } -} - -Response = Message(Response) - - -ErrorView.propTypes = { - flow: PropTypes.object.isRequired, -} - -export function ErrorView({ flow }) { - return ( -
-
- {flow.error.msg} -
- {formatTimeStamp(flow.error.timestamp)} -
-
-
- ) -} diff --git a/web/src/js/components/FlowView/Messages.tsx b/web/src/js/components/FlowView/Messages.tsx new file mode 100644 index 000000000..10fcc53a5 --- /dev/null +++ b/web/src/js/components/FlowView/Messages.tsx @@ -0,0 +1,198 @@ +import React from 'react' + +import { RequestUtils, isValidHttpVersion, parseUrl } from '../../flow/utils' +import { formatTimeStamp } from '../../utils' +import ContentView from '../ContentView' +import ContentViewOptions from '../ContentView/ContentViewOptions' +import ValidateEditor from '../ValueEditor/ValidateEditor' +import ValueEditor from '../ValueEditor/ValueEditor' +import HideInStatic from '../common/HideInStatic' + +import Headers from './Headers' +import { updateEdit as updateFlow } from '../../ducks/ui/flow' +import { uploadContent } from '../../ducks/flows' +import ToggleEdit from './ToggleEdit' +import { useAppDispatch, useAppSelector } from "../../ducks"; +import { HTTPFlow, HTTPMessage } from '../../flow' + + +type RequestLineProps = { + flow: HTTPFlow, + readonly: boolean, +} + +function RequestLine({ flow, readonly }: RequestLineProps) { + const dispatch = useAppDispatch() + + return ( +
+
+ dispatch(updateFlow({ request: { method } }))} + /> +   + dispatch(updateFlow({ request: {path: '', ...parseUrl(url)}}))} + isValid={url => !!parseUrl(url).host} + /> +   + dispatch(updateFlow({ request: { http_version } }))} + isValid={isValidHttpVersion} + /> +
+
+ ) +} + +type ResponseLineProps = { + flow: HTTPFlow, + readonly: boolean, +} + +function ResponseLine({ flow, readonly }: ResponseLineProps) { + const dispatch = useAppDispatch() + + return ( +
+ dispatch(updateFlow({ response: { http_version: nextVer } }))} + isValid={isValidHttpVersion} + /> +   + dispatch(updateFlow({ response: { code: parseInt(code) } }))} + isValid={code => /^\d+$/.test(code)} + /> +   + dispatch(updateFlow({ response: { msg } }))} + /> +
+ ) +} + +export function Request() { + const dispatch = useAppDispatch(), + flow = useAppSelector(state => state.ui.flow.modifiedFlow || state.flows.byId[state.flows.selected[0]]), + isEdit = useAppSelector(state => !!state.ui.flow.modifiedFlow) + + let noContent = !isEdit && (flow.request.contentLength == 0 || flow.request.contentLength == null) + return ( +
+
+ + + dispatch(updateFlow({ request: { headers } }))} + /> + +
+ dispatch(updateFlow({ request: {content}}))} + message={flow.request}/> + +
+ dispatch(updateFlow({ request: { trailers } }))} + type='trailers' + /> +
+ + {!noContent && +
+ dispatch(uploadContent(flow, content, "request"))}/> +
+ } +
+
+ ) +} + +export function Response() { + const dispatch = useAppDispatch(), + flow = useAppSelector(state => state.ui.flow.modifiedFlow || state.flows.byId[state.flows.selected[0]]), + isEdit = useAppSelector(state => !!state.ui.flow.modifiedFlow) + + let noContent = !isEdit && (flow.response.contentLength == 0 || flow.response.contentLength == null) + + return ( +
+
+ + + dispatch(updateFlow({ response: { headers } }))} + /> +
+ dispatch(updateFlow({ response: {content}}))} + message={flow.response} + /> +
+ dispatch(updateFlow({ response: { trailers } }))} + type='trailers' + /> +
+ + {!noContent && +
+ dispatch(uploadContent(flow, content, "response"))} /> +
+ } +
+
+ ) +} + +type ErrorViewProps = { + flow: HTTPFlow +} + +export function ErrorView({ flow }: ErrorViewProps) { + return ( +
+
+ {flow.error?.msg} +
+ {formatTimeStamp(flow.error?.timestamp)} +
+
+
+ ) +} diff --git a/web/src/js/components/FlowView/Nav.jsx b/web/src/js/components/FlowView/Nav.tsx similarity index 64% rename from web/src/js/components/FlowView/Nav.jsx rename to web/src/js/components/FlowView/Nav.tsx index 022f2f2be..82a66c999 100644 --- a/web/src/js/components/FlowView/Nav.jsx +++ b/web/src/js/components/FlowView/Nav.tsx @@ -1,16 +1,14 @@ import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' import classnames from 'classnames' import _ from 'lodash' -NavAction.propTypes = { - icon: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - onClick: PropTypes.func.isRequired, +type NavActionProps = { + icon: string, + title: string, + onClick: (e: any) => void, } -export function NavAction({ icon, title, onClick }) { +export function NavAction({ icon, title, onClick }: NavActionProps) { return ( void, } -export default function Nav({ active, tabs, onSelectTab }) { +export default function Nav({ active, tabs, onSelectTab }: NavProps) { return ( -
+ diff --git a/web/src/js/components/Header/FlowMenu.jsx b/web/src/js/components/Header/FlowMenu.tsx similarity index 66% rename from web/src/js/components/Header/FlowMenu.jsx rename to web/src/js/components/Header/FlowMenu.tsx index d4695e0e7..748c7171e 100644 --- a/web/src/js/components/Header/FlowMenu.jsx +++ b/web/src/js/components/Header/FlowMenu.tsx @@ -1,24 +1,23 @@ import React from "react" -import PropTypes from 'prop-types' -import { connect } from "react-redux" import Button from "../common/Button" import { MessageUtils } from "../../flow/utils.js" -import * as flowsActions from "../../ducks/flows" import HideInStatic from "../common/HideInStatic"; +import { useAppDispatch, useAppSelector } from "../../ducks"; +import { + resume as resumeFlow, + replay as replayFlow, + duplicate as duplicateFlow, + revert as revertFlow, + remove as removeFlow, + kill as killFlow +} from "../../ducks/flows" FlowMenu.title = 'Flow' -FlowMenu.propTypes = { - flow: PropTypes.object, - resumeFlow: PropTypes.func.isRequired, - killFlow: PropTypes.func.isRequired, - replayFlow: PropTypes.func.isRequired, - duplicateFlow: PropTypes.func.isRequired, - removeFlow: PropTypes.func.isRequired, - revertFlow: PropTypes.func.isRequired -} +export default function FlowMenu() { + const dispatch = useAppDispatch(), + flow = useAppSelector(state => state.flows.byId[state.flows.selected[0]]) -export function FlowMenu({ flow, resumeFlow, killFlow, replayFlow, duplicateFlow, removeFlow, revertFlow }) { if (!flow) return
return ( @@ -27,19 +26,19 @@ export function FlowMenu({ flow, resumeFlow, killFlow, replayFlow, duplicateFlow
@@ -61,11 +60,11 @@ export function FlowMenu({ flow, resumeFlow, killFlow, replayFlow, duplicateFlow
@@ -75,17 +74,3 @@ export function FlowMenu({ flow, resumeFlow, killFlow, replayFlow, duplicateFlow
) } - -export default connect( - state => ({ - flow: state.flows.byId[state.flows.selected[0]], - }), - { - resumeFlow: flowsActions.resume, - killFlow: flowsActions.kill, - replayFlow: flowsActions.replay, - duplicateFlow: flowsActions.duplicate, - removeFlow: flowsActions.remove, - revertFlow: flowsActions.revert, - } -)(FlowMenu) diff --git a/web/src/js/components/Header/OptionMenu.jsx b/web/src/js/components/Header/OptionMenu.tsx similarity index 86% rename from web/src/js/components/Header/OptionMenu.jsx rename to web/src/js/components/Header/OptionMenu.tsx index 7c1173614..b80f84725 100644 --- a/web/src/js/components/Header/OptionMenu.jsx +++ b/web/src/js/components/Header/OptionMenu.tsx @@ -1,21 +1,24 @@ import React from "react" -import { connect } from "react-redux" import { EventlogToggle, OptionsToggle } from "./MenuToggle" import Button from "../common/Button" import DocsLink from "../common/DocsLink" import HideInStatic from "../common/HideInStatic"; import * as modalActions from "../../ducks/ui/modal" +import { useAppDispatch } from "../../ducks"; OptionMenu.title = 'Options' -function OptionMenu({ openOptions }) { +export default function OptionMenu() { + const dispatch = useAppDispatch() + const openOptions = () => modalActions.setActiveModal('OptionModal') + return (
@@ -47,10 +50,3 @@ function OptionMenu({ openOptions }) {
) } - -export default connect( - null, - { - openOptions: () => modalActions.setActiveModal('OptionModal') - } -)(OptionMenu) diff --git a/web/src/js/components/MainView.jsx b/web/src/js/components/MainView.tsx similarity index 52% rename from web/src/js/components/MainView.jsx rename to web/src/js/components/MainView.tsx index 03bfce7fd..922833529 100644 --- a/web/src/js/components/MainView.jsx +++ b/web/src/js/components/MainView.tsx @@ -1,15 +1,11 @@ import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' import Splitter from './common/Splitter' import FlowTable from './FlowTable' import FlowView from './FlowView' +import {useAppSelector} from "../ducks"; -MainView.propTypes = { - hasSelection: PropTypes.bool.isRequired, -} - -function MainView({ hasSelection }) { +export default function MainView() { + const hasSelection = useAppSelector(state => !!state.flows.byId[state.flows.selected[0]]) return (
@@ -18,10 +14,3 @@ function MainView({ hasSelection }) {
) } - -export default connect( - state => ({ - hasSelection: !!state.flows.byId[state.flows.selected[0]] - }), - {} -)(MainView) diff --git a/web/src/js/components/Modal/Modal.jsx b/web/src/js/components/Modal/Modal.jsx index 88e811568..11487f9b8 100644 --- a/web/src/js/components/Modal/Modal.jsx +++ b/web/src/js/components/Modal/Modal.jsx @@ -1,24 +1,13 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' +import React from 'react' import ModalList from './ModalList' +import { useAppSelector } from "../../ducks"; -class PureModal extends Component { - constructor(props, context) { - super(props, context) - } +export default function PureModal() { + const activeModal = useAppSelector(state => state.ui.modal.activeModal) + const ActiveModal = ModalList.find(m => m.name === activeModal ) - render() { - const { activeModal } = this.props - const ActiveModal = ModalList.find(m => m.name === activeModal ) - return( - activeModal ? :
- ) - } + return( + activeModal ? :
+ ) } - -export default connect( - state => ({ - activeModal: state.ui.modal.activeModal - }) -)(PureModal) diff --git a/web/src/js/components/Modal/ModalList.jsx b/web/src/js/components/Modal/ModalList.tsx similarity index 100% rename from web/src/js/components/Modal/ModalList.jsx rename to web/src/js/components/Modal/ModalList.tsx diff --git a/web/src/js/components/ProxyApp.jsx b/web/src/js/components/ProxyApp.jsx deleted file mode 100644 index 7ea21ee28..000000000 --- a/web/src/js/components/ProxyApp.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - -import { onKeyDown } from '../ducks/ui/keyboard' -import MainView from './MainView' -import Header from './Header' -import CommandBar from './CommandBar' -import EventLog from './EventLog' -import Footer from './Footer' -import Modal from './Modal/Modal' - -class ProxyAppMain extends Component { - - componentDidMount() { - window.addEventListener('keydown', this.props.onKeyDown); - } - - componentWillUnmount() { - window.removeEventListener('keydown', this.props.onKeyDown); - } - - render() { - const { showEventLog } = this.props - return ( -
-
- - - {showEventLog && ( - - )} -
- -
- ) - } -} - -export default connect( - state => ({ - showEventLog: state.eventLog.visible, - }), - { - onKeyDown, - } -)(ProxyAppMain) diff --git a/web/src/js/components/ProxyApp.tsx b/web/src/js/components/ProxyApp.tsx new file mode 100644 index 000000000..a1d8a0173 --- /dev/null +++ b/web/src/js/components/ProxyApp.tsx @@ -0,0 +1,35 @@ +import React, { useEffect } from 'react' + +import { onKeyDown } from '../ducks/ui/keyboard' +import MainView from './MainView' +import Header from './Header' +import CommandBar from './CommandBar' +import EventLog from './EventLog' +import Footer from './Footer' +import Modal from './Modal/Modal' +import {useAppDispatch, useAppSelector} from "../ducks"; + +export default function ProxyAppMain() { + const dispatch = useAppDispatch(), + showEventLog = useAppSelector(state => state.eventLog.visible) + + useEffect(() => { + window.addEventListener('keydown', (e) => dispatch(onKeyDown(e))); + return function cleanup() { + window.removeEventListener('keydown', (e) => dispatch(onKeyDown(e))); + } + }) + + return ( +
+
+ + + {showEventLog && ( + + )} +
+ +
+ ) +} diff --git a/web/src/js/components/ValueEditor/ValidateEditor.jsx b/web/src/js/components/ValueEditor/ValidateEditor.tsx old mode 100755 new mode 100644 similarity index 78% rename from web/src/js/components/ValueEditor/ValidateEditor.jsx rename to web/src/js/components/ValueEditor/ValidateEditor.tsx index 6a886930e..947f12a11 --- a/web/src/js/components/ValueEditor/ValidateEditor.jsx +++ b/web/src/js/components/ValueEditor/ValidateEditor.tsx @@ -1,19 +1,20 @@ import React, { Component } from 'react' -import PropTypes from 'prop-types' import ValueEditor from './ValueEditor' import classnames from 'classnames' +type ValidateEditorProps = { + content: string | undefined, + readonly: boolean, + onDone: (content: string) => void, + className?: string, + isValid: (content: string) => boolean, +} -export default class ValidateEditor extends Component { - - static propTypes = { - content: PropTypes.string.isRequired, - readonly: PropTypes.bool, - onDone: PropTypes.func.isRequired, - className: PropTypes.string, - isValid: PropTypes.func.isRequired, - } +type ValidateEditorStates = { + valid: boolean, +} +export default class ValidateEditor extends Component { constructor(props) { super(props) this.state = { valid: props.isValid(props.content) } diff --git a/web/src/js/components/common/DocsLink.tsx b/web/src/js/components/common/DocsLink.tsx index f34c01953..a336bfaf8 100644 --- a/web/src/js/components/common/DocsLink.tsx +++ b/web/src/js/components/common/DocsLink.tsx @@ -1,7 +1,7 @@ import React from "react" type DocLinkProps = { - children: React.ReactNode, + children?: React.ReactNode, resource: string } diff --git a/web/src/js/components/common/HideInStatic.tsx b/web/src/js/components/common/HideInStatic.tsx index 3f3ff6a1d..668a5cb22 100644 --- a/web/src/js/components/common/HideInStatic.tsx +++ b/web/src/js/components/common/HideInStatic.tsx @@ -5,5 +5,5 @@ type HideInStaticProps = { } export default function HideInStatic({ children }: HideInStaticProps) { - return (window.MITMWEB_CONF && window.MITMWEB_CONF.static) ? null : [children] + return (window.MITMWEB_CONF && window.MITMWEB_CONF.static) ? null : <>{[children]} } diff --git a/web/src/js/components/common/ToggleButton.tsx b/web/src/js/components/common/ToggleButton.tsx index 07345479a..a6fcec7bb 100644 --- a/web/src/js/components/common/ToggleButton.tsx +++ b/web/src/js/components/common/ToggleButton.tsx @@ -2,7 +2,7 @@ import React from 'react' type ToggleButtonProps = { checked: boolean, - onToggle: () => void, + onToggle: () => any, text: string } diff --git a/web/src/js/components/common/ToggleInputButton.tsx b/web/src/js/components/common/ToggleInputButton.tsx index 066cef959..ce7aaf96d 100644 --- a/web/src/js/components/common/ToggleInputButton.tsx +++ b/web/src/js/components/common/ToggleInputButton.tsx @@ -5,7 +5,7 @@ import { Key } from '../../utils' type ToggleInputButtonProps = { name: string, txt: string, - onToggleChanged: (string) => void, + onToggleChanged: Function, checked: boolean, placeholder: string, inputType: string, diff --git a/web/src/js/ducks/ui/header.js b/web/src/js/ducks/ui/header.ts similarity index 100% rename from web/src/js/ducks/ui/header.js rename to web/src/js/ducks/ui/header.ts diff --git a/web/src/js/ducks/ui/modal.js b/web/src/js/ducks/ui/modal.ts similarity index 100% rename from web/src/js/ducks/ui/modal.js rename to web/src/js/ducks/ui/modal.ts diff --git a/web/src/js/ducks/ui/optionsEditor.js b/web/src/js/ducks/ui/optionsEditor.ts similarity index 100% rename from web/src/js/ducks/ui/optionsEditor.js rename to web/src/js/ducks/ui/optionsEditor.ts diff --git a/web/src/js/flow.ts b/web/src/js/flow.ts index deaeed2b8..50cacc862 100644 --- a/web/src/js/flow.ts +++ b/web/src/js/flow.ts @@ -67,6 +67,7 @@ export interface HTTPMessage { trailers?: Headers contentLength: number contentHash: string + content?: string timestamp_start: number timestamp_end?: number } diff --git a/web/src/js/utils.ts b/web/src/js/utils.ts index d2e101d25..0b2ae75b4 100644 --- a/web/src/js/utils.ts +++ b/web/src/js/utils.ts @@ -59,7 +59,7 @@ export var formatTimeDelta = function (milliseconds) { export var formatTimeStamp = function (seconds, utc_to_local=true) { var utc = new Date(seconds * 1000); - if (utc_to_local && !process.env.JEST_WORKER_ID) { + if (utc_to_local) { var local = utc.getTime() - utc.getTimezoneOffset() * 60 * 1000; var ts = new Date(local).toISOString(); } else {