-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
HttpMessage.tsx
121 lines (113 loc) · 4.9 KB
/
HttpMessage.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import React, {useCallback, useMemo, useRef, useState} from "react";
import {HTTPFlow, HTTPMessage} from "../../flow";
import {useAppDispatch, useAppSelector} from "../../ducks";
import {setContentViewFor} from "../../ducks/ui/flow";
import {ContentViewData, useContent} from "./useContent";
import {MessageUtils} from "../../flow/utils";
import FileChooser from "../common/FileChooser";
import * as flowActions from "../../ducks/flows";
import {uploadContent} from "../../ducks/flows";
import Button from "../common/Button";
import CodeEditor from "./CodeEditor";
import LineRenderer from "./LineRenderer";
import ViewSelector from "./ViewSelector";
type HttpMessageProps = {
flow: HTTPFlow
message: HTTPMessage
}
export default function HttpMessage({flow, message}: HttpMessageProps) {
const dispatch = useAppDispatch();
const part = flow.request === message ? "request" : "response";
const contentView = useAppSelector(state => state.ui.flow.contentViewFor[flow.id + part] || "Auto");
const editorRef = useRef<CodeEditor>(null);
const [maxLines, setMaxLines] = useState<number>(useAppSelector(state => state.options.content_view_lines_cutoff));
const showMore = useCallback(() => setMaxLines(Math.max(1024, maxLines * 2)), [maxLines]);
const [edit, setEdit] = useState<boolean>(false);
let url;
if (edit) {
url = MessageUtils.getContentURL(flow, message);
} else {
url = MessageUtils.getContentURL(flow, message, contentView, maxLines + 1);
}
let content = useContent(url, message.contentHash);
if (flow.request.method === "GET" && edit) {
const params = new URLSearchParams(new URL(`${flow.request.scheme}://${flow.request.host}/${flow.request.path}`).search);
content = Array.from(params.entries())
.reduce((acc, [key, value]) => [...acc, `${key}=${value}`], [])
.join("\n");
}
const contentViewData = useMemo<ContentViewData | undefined>(() => {
if (content && !edit) {
try {
return JSON.parse(content)
} catch (e) {
const err: ContentViewData = {"description": "Network Error", lines: [[["error", `${content}`]]]};
return err;
}
} else {
return undefined
}
}, [content]);
if (edit) {
const save = async () => {
let content = editorRef.current?.getContent();
await dispatch(flowActions.update(flow, {
[part]: flow.request.method === 'GET' ? {
query: content?.split("\n").map(item => item.split('='))
}: {
content
}
}));
setEdit(false);
}
return (
<div className="contentview" key="edit">
<div className="controls">
<h5>[Editing]</h5>
<Button onClick={save} icon="fa-check text-success" className="btn-xs">Done</Button>
<Button onClick={() => setEdit(false)} icon="fa-times text-danger"
className="btn-xs">Cancel</Button>
</div>
<CodeEditor ref={editorRef} initialContent={content || ""}/>
</div>
);
} else {
let desc = contentViewData ? contentViewData.description : "Loading...";
return (
<div className="contentview" key="view">
<div className="controls">
<h5>{desc}</h5>
<Button onClick={() => setEdit(true)} icon="fa-edit" className="btn-xs">Edit</Button>
<FileChooser
icon="fa-upload"
text="Replace"
title="Upload a file to replace the content."
onOpenFile={content => dispatch(uploadContent(flow, content, part))}
className="btn btn-default btn-xs"/>
<ViewSelector value={contentView}
onChange={cv => dispatch(setContentViewFor(flow.id + part, cv))}/>
</div>
{ViewImage.matches(message) && <ViewImage flow={flow} message={message}/>}
<LineRenderer lines={contentViewData?.lines || []}
maxLines={maxLines}
showMore={showMore}/>
</div>
);
}
}
const isImage = /^image\/(png|jpe?g|gif|webp|vnc.microsoft.icon|x-icon)$/i
ViewImage.matches = msg => isImage.test(MessageUtils.getContentType(msg) || "")
type ViewImageProps = {
flow: HTTPFlow,
message: HTTPMessage,
}
export function ViewImage({flow, message}: ViewImageProps) {
return (
<div className="flowview-image">
<img src={MessageUtils.getContentURL(flow, message)} alt="preview" className="img-thumbnail"/>
</div>
)
}