ahooks#useDebounceFn TypeScript Examples
The following examples show how to use
ahooks#useDebounceFn.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx From shippo with MIT License | 4 votes |
Complete: React.FC = () => {
const [options, setOptions] = useState<SelectProps<object>['options']>([])
const { run: handleSearch } = useDebounceFn(
(value: string) => {
if (!value) return
jsonp<any>('https://www.baidu.com/sugrec?prod=pc&wd=' + value, { jsonpCallback: 'cb' })
.then((res) => res.json())
.then((res) => {
console.log(res)
if (res && res.g && Array.isArray(res.g)) {
res = res.g.map((r: any) => ({
value: r.q,
label: (
<div
style={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<span>
<a
href={`https://www.baidu.com/s?ie=utf-8&wd=${r.q}`}
target="_blank"
rel="noopener noreferrer"
>
{r.q}
</a>
</span>
</div>
),
}))
setOptions(res)
} else {
setOptions([])
}
})
},
{
wait: 500,
}
)
const onSelect = (value: string) => {
console.log('onSelect', value)
}
return (
<div style={{ textAlign: 'center', height: '40%', position: 'relative' }}>
<AutoComplete
dropdownMatchSelectWidth={252}
style={{
width: 650,
position: 'absolute',
bottom: 0,
left: '50%',
transform: 'translateX(-50%)',
}}
options={options}
onSelect={onSelect}
onSearch={handleSearch}
>
<StyledSearch
size="large"
enterButton="百度一下"
allowClear
onSearch={(value, event) => {
console.log(value, event)
if (!value) return
window.open('https://www.baidu.com/s?ie=utf-8&wd=' + value)
}}
/>
</AutoComplete>
</div>
)
}
Example #2
Source File: HTTPFlowTable.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
HTTPFlowTable: React.FC<HTTPFlowTableProp> = (props) => {
const [data, setData, getData] = useGetState<HTTPFlow[]>([])
const [params, setParams] = useState<YakQueryHTTPFlowRequest>(
props.params || {SourceType: "mitm"}
)
const [pagination, setPagination] = useState<PaginationSchema>({
Limit: OFFSET_LIMIT,
Order: "desc",
OrderBy: "created_at",
Page: 1
});
// const [autoReload, setAutoReload, getAutoReload] = useGetState(false);
const autoReloadRef = useRef<boolean>(false);
const autoReload = autoReloadRef.current;
const setAutoReload = (b: boolean) => {
autoReloadRef.current = b
};
const getAutoReload = () => autoReloadRef.current;
const [total, setTotal] = useState<number>(0)
const [loading, setLoading] = useState(false)
const [selected, setSelected, getSelected] = useGetState<HTTPFlow>()
const [_lastSelected, setLastSelected, getLastSelected] = useGetState<HTTPFlow>()
const [compareLeft, setCompareLeft] = useState<CompateData>({content: '', language: 'http'})
const [compareRight, setCompareRight] = useState<CompateData>({content: '', language: 'http'})
const [compareState, setCompareState] = useState(0)
const [tableContentHeight, setTableContentHeight, getTableContentHeight] = useGetState<number>(0);
// 用于记录适合
const [_scrollY, setScrollYRaw, getScrollY] = useGetState(0)
const setScrollY = useThrottleFn(setScrollYRaw, {wait: 300}).run
// 如果这个大于等于 0 ,就 Lock 住,否则忽略
const [_trigger, setLockedScroll, getLockedScroll] = useGetState(-1);
const lockScrollTimeout = (size: number, timeout: number) => {
setLockedScroll(size)
setTimeout(() => setLockedScroll(-1), timeout)
}
const tableRef = useRef(null)
const ref = useHotkeys('ctrl+r, enter', e => {
const selected = getSelected()
if (selected) {
ipcRenderer.invoke("send-to-tab", {
type: "fuzzer",
data: {
isHttps: selected?.IsHTTPS,
request: new Buffer(selected.Request).toString()
}
})
}
})
// 使用上下箭头
useHotkeys("up", () => {
setLastSelected(getSelected())
const data = getData();
if (data.length <= 0) {
return
}
if (!getSelected()) {
setSelected(data[0])
return
}
const expected = parseInt(`${parseInt(`${(getSelected()?.Id as number)}`) + 1}`);
// 如果上点的话,应该是选择更新的内容
for (let i = 0; i < data.length; i++) {
let current = parseInt(`${data[i]?.Id}`);
if (current === expected) {
setSelected(data[i])
return
}
}
setSelected(undefined)
})
useHotkeys("down", () => {
setLastSelected(getSelected())
const data = getData();
if (data.length <= 0) {
return
}
if (!getSelected()) {
setSelected(data[0])
return
}
// 如果上点的话,应该是选择更新的内容
for (let i = 0; i < data.length; i++) {
if (data[i]?.Id == (getSelected()?.Id as number) - 1) {
setSelected(data[i])
return
}
}
setSelected(undefined)
})
// 向主页发送对比数据
useEffect(() => {
if (compareLeft.content) {
const params = {info: compareLeft, type: 1}
setCompareState(compareState === 0 ? 1 : 0)
ipcRenderer.invoke("add-data-compare", params)
}
}, [compareLeft])
useEffect(() => {
if (compareRight.content) {
const params = {info: compareRight, type: 2}
setCompareState(compareState === 0 ? 2 : 0)
ipcRenderer.invoke("add-data-compare", params)
}
}, [compareRight])
const update = useMemoizedFn((
page?: number,
limit?: number,
order?: string,
orderBy?: string,
sourceType?: string,
noLoading?: boolean
) => {
const paginationProps = {
Page: page || 1,
Limit: limit || pagination.Limit,
Order: order || "desc",
OrderBy: orderBy || "id"
}
if (!noLoading) {
setLoading(true)
// setAutoReload(false)
}
// yakQueryHTTPFlow({
// SourceType: sourceType, ...params,
// Pagination: {...paginationProps},
// })
ipcRenderer
.invoke("QueryHTTPFlows", {
SourceType: sourceType,
...params,
Pagination: {...paginationProps}
})
.then((rsp: YakQueryHTTPFlowResponse) => {
setData((rsp?.Data || []))
setPagination(rsp.Pagination)
setTotal(rsp.Total)
})
.catch((e: any) => {
failed(`query HTTP Flow failed: ${e}`)
})
.finally(() => setTimeout(() => setLoading(false), 300))
})
const getNewestId = useMemoizedFn(() => {
let max = 0;
(getData() || []).forEach(e => {
const id = parseInt(`${e.Id}`)
if (id >= max) {
max = id
}
})
return max
})
const getOldestId = useMemoizedFn(() => {
if (getData().length <= 0) {
return 0
}
let min = parseInt(`${getData()[0].Id}`);
(getData() || []).forEach(e => {
const id = parseInt(`${e.Id}`)
if (id <= min) {
min = id
}
})
return min
})
// 第一次启动的时候加载一下
useEffect(() => {
update(1)
}, [])
const scrollTableTo = useMemoizedFn((size: number) => {
if (!tableRef || !tableRef.current) return
const table = tableRef.current as unknown as {
scrollTop: (number) => any,
scrollLeft: (number) => any,
}
table.scrollTop(size)
})
const scrollUpdateTop = useDebounceFn(useMemoizedFn(() => {
const paginationProps = {
Page: 1,
Limit: OFFSET_STEP,
Order: "desc",
OrderBy: "id"
}
const offsetId = getNewestId()
console.info("触顶:", offsetId)
// 查询数据
ipcRenderer
.invoke("QueryHTTPFlows", {
SourceType: "mitm",
...params,
AfterId: offsetId, // 用于计算增量的
Pagination: {...paginationProps}
})
.then((rsp: YakQueryHTTPFlowResponse) => {
const offsetDeltaData = (rsp?.Data || [])
if (offsetDeltaData.length <= 0) {
// 没有增量数据
return
}
setLoading(true)
let offsetData = offsetDeltaData.concat(data);
if (offsetData.length > MAX_ROW_COUNT) {
offsetData = offsetData.splice(0, MAX_ROW_COUNT)
}
setData(offsetData);
scrollTableTo((offsetDeltaData.length + 1) * ROW_HEIGHT)
})
.catch((e: any) => {
failed(`query HTTP Flow failed: ${e}`)
})
.finally(() => setTimeout(() => setLoading(false), 200))
}), {wait: 600, leading: true, trailing: false}).run
const scrollUpdateButt = useDebounceFn(useMemoizedFn((tableClientHeight: number) => {
const paginationProps = {
Page: 1,
Limit: OFFSET_STEP,
Order: "desc",
OrderBy: "id"
}
const offsetId = getOldestId();
console.info("触底:", offsetId)
// 查询数据
ipcRenderer
.invoke("QueryHTTPFlows", {
SourceType: "mitm",
...params,
BeforeId: offsetId, // 用于计算增量的
Pagination: {...paginationProps}
})
.then((rsp: YakQueryHTTPFlowResponse) => {
const offsetDeltaData = (rsp?.Data || [])
if (offsetDeltaData.length <= 0) {
// 没有增量数据
return
}
setLoading(true)
const originDataLength = data.length;
let offsetData = data.concat(offsetDeltaData);
let metMax = false
const originOffsetLength = offsetData.length;
if (originOffsetLength > MAX_ROW_COUNT) {
metMax = true
offsetData = offsetData.splice(originOffsetLength - MAX_ROW_COUNT, MAX_ROW_COUNT)
}
setData(offsetData);
setTimeout(() => {
if (!metMax) {
// 没有丢结果的裁剪问题
scrollTableTo((originDataLength + 1) * ROW_HEIGHT - tableClientHeight)
} else {
// 丢了结果之后的裁剪计算
const a = originOffsetLength - offsetDeltaData.length;
scrollTableTo((originDataLength + 1 + MAX_ROW_COUNT - originOffsetLength) * ROW_HEIGHT - tableClientHeight)
}
}, 50)
})
.catch((e: any) => {
failed(`query HTTP Flow failed: ${e}`)
}).finally(() => setTimeout(() => setLoading(false), 60))
}), {wait: 600, leading: true, trailing: false}).run
const sortFilter = useMemoizedFn((column: string, type: any) => {
const keyRelation: any = {
UpdatedAt: "updated_at",
BodyLength: "body_length",
StatusCode: "status_code"
}
if (column && type) {
update(1, OFFSET_LIMIT, type, keyRelation[column])
} else {
update(1, OFFSET_LIMIT)
}
})
// 这是用来设置选中坐标的,不需要做防抖
useEffect(() => {
if (!getLastSelected() || !getSelected()) {
return
}
const lastSelected = getLastSelected() as HTTPFlow;
const up = parseInt(`${lastSelected?.Id}`) < parseInt(`${selected?.Id}`)
// if (up) {
// console.info("up")
// } else {
// console.info("down")
// }
// console.info(lastSelected.Id, selected?.Id)
const screenRowCount = Math.floor(getTableContentHeight() / ROW_HEIGHT) - 1
if (!autoReload) {
let count = 0;
const data = getData();
for (let i = 0; i < data.length; i++) {
if (data[i].Id != getSelected()?.Id) {
count++
} else {
break
}
}
let minCount = count
if (minCount < 0) {
minCount = 0
}
const viewHeightMin = getScrollY() + tableContentHeight
const viewHeightMax = getScrollY() + tableContentHeight * 2
const minHeight = minCount * ROW_HEIGHT;
const maxHeight = minHeight + tableContentHeight
const maxHeightBottom = minHeight + tableContentHeight + 3 * ROW_HEIGHT
// console.info("top: ", minHeight, "maxHeight: ", maxHeight, "maxHeightBottom: ", maxHeightBottom)
// console.info("viewTop: ", viewHeightMin, "viewButtom: ", viewHeightMax)
if (maxHeight < viewHeightMin) {
// 往下滚动
scrollTableTo(minHeight)
return
}
if (maxHeightBottom > viewHeightMax) {
// 上滚动
const offset = minHeight - (screenRowCount - 2) * ROW_HEIGHT;
// console.info(screenRowCount, minHeight, minHeight - (screenRowCount - 1) * ROW_HEIGHT)
if (offset > 0) {
scrollTableTo(offset)
}
return
}
}
}, [selected])
// 给设置做防抖
useDebounceEffect(() => {
props.onSelected && props.onSelected(selected)
}, [selected], {wait: 400, trailing: true, leading: true})
useEffect(() => {
if (autoReload) {
const id = setInterval(() => {
update(1, undefined, "desc", undefined, undefined, true)
}, 1000)
return () => {
clearInterval(id)
}
}
}, [autoReload])
return (
// <AutoCard bodyStyle={{padding: 0, margin: 0}} bordered={false}>
<div ref={ref as Ref<any>} tabIndex={-1}
style={{width: "100%", height: "100%", overflow: "hidden"}}
>
<ReactResizeDetector
onResize={(width, height) => {
if (!width || !height) {
return
}
setTableContentHeight(height - 38)
}}
handleWidth={true} handleHeight={true} refreshMode={"debounce"} refreshRate={50}/>
{!props.noHeader && (
<PageHeader
title={"HTTP History"}
subTitle={
<Space>
{"所有相关请求都在这里"}
<Button
icon={<ReloadOutlined/>}
type={"link"}
onClick={(e) => {
update(1)
}}
/>
</Space>
}
extra={[
<Space>
<Form.Item label={"选择 HTTP History 类型"} style={{marginBottom: 0}}>
<Select
mode={"multiple"}
value={params.SourceType}
style={{minWidth: 200}}
onChange={(e) => {
setParams({...params, SourceType: e})
setLoading(true)
setTimeout(() => {
update(1, undefined, undefined, undefined, e)
}, 200)
}}
>
<Select.Option value={"mitm"}>mitm: 中间人劫持</Select.Option>
<Select.Option value={"fuzzer"}>
fuzzer: 模糊测试分析
</Select.Option>
</Select>
</Form.Item>
<Popconfirm
title={"确定想要删除所有记录吗?不可恢复"}
onConfirm={(e) => {
ipcRenderer.invoke("delete-http-flows-all")
setLoading(true)
info("正在删除...如自动刷新失败请手动刷新")
setTimeout(() => {
update(1)
if (props.onSelected) props.onSelected(undefined)
}, 400)
}}
>
<Button danger={true}>清除全部历史记录?</Button>
</Popconfirm>
</Space>
]}
/>
)}
<Row style={{margin: "5px 0 5px 5px"}}>
<Col span={12}>
<Space>
<span>HTTP History</span>
<Button
icon={<ReloadOutlined/>}
type={"link"}
size={"small"}
onClick={(e) => {
update(1, undefined, "desc")
}}
/>
{/* <Space>
自动刷新:
<Switch size={"small"} checked={autoReload} onChange={setAutoReload}/>
</Space> */}
<Input.Search
placeholder={"URL关键字"}
enterButton={true}
size={"small"}
style={{width: 170}}
value={params.SearchURL}
onChange={(e) => {
setParams({...params, SearchURL: e.target.value})
}}
onSearch={(v) => {
update(1)
}}
/>
{props.noHeader && (
<Popconfirm
title={"确定想要删除所有记录吗?不可恢复"}
onConfirm={(e) => {
ipcRenderer.invoke("delete-http-flows-all")
setLoading(true)
info("正在删除...如自动刷新失败请手动刷新")
setCompareLeft({content: '', language: 'http'})
setCompareRight({content: '', language: 'http'})
setCompareState(0)
setTimeout(() => {
update(1)
if (props.onSelected) props.onSelected(undefined)
}, 400)
}}
>
<Button danger={true} size={"small"}>
删除历史记录
</Button>
</Popconfirm>
)}
{/*{autoReload && <Tag color={"green"}>自动刷新中...</Tag>}*/}
</Space>
</Col>
<Col span={12} style={{textAlign: "right"}}>
<Tag>{total} Records</Tag>
</Col>
</Row>
<TableResizableColumn
tableRef={tableRef}
virtualized={true}
className={"httpFlowTable"}
loading={loading}
columns={[
{
dataKey: "Id",
width: 80,
headRender: () => "序号",
cellRender: ({rowData, dataKey, ...props}: any) => {
return `${rowData[dataKey] <= 0 ? "..." : rowData[dataKey]}`
}
},
{
dataKey: "Method",
width: 70,
headRender: (params1: any) => {
return (
<div
style={{display: "flex", justifyContent: "space-between"}}
>
方法
<Popover
placement='bottom'
trigger='click'
content={
params &&
setParams && (
<HTTLFlowFilterDropdownForms
label={"搜索方法"}
params={params}
setParams={setParams}
filterName={"Methods"}
autoCompletions={["GET", "POST", "HEAD"]}
submitFilter={() => update(1)}
/>
)
}
>
<Button
style={{
paddingLeft: 4, paddingRight: 4, marginLeft: 4,
color: !!params.Methods ? undefined : "gray",
}}
type={!!params.Methods ? "primary" : "link"} size={"small"}
icon={<SearchOutlined/>}
/>
</Popover>
</div>
)
},
cellRender: ({rowData, dataKey, ...props}: any) => {
// return (
// <Tag color={"geekblue"} style={{marginRight: 20}}>
// {rowData[dataKey]}
// </Tag>
// )
return rowData[dataKey]
}
},
{
dataKey: "StatusCode",
width: 100,
sortable: true,
headRender: () => {
return (
<div
style={{display: "inline-flex"}}
>
状态码
<Popover
placement='bottom'
trigger='click'
content={
params &&
setParams && (
<HTTLFlowFilterDropdownForms
label={"搜索状态码"}
params={params}
setParams={setParams}
filterName={"StatusCode"}
autoCompletions={[
"200",
"300-305",
"400-404",
"500-502",
"200-299",
"300-399",
"400-499"
]}
submitFilter={() => update(1)}
/>
)
}
>
<Button
style={{
paddingLeft: 4, paddingRight: 4, marginLeft: 4,
color: !!params.StatusCode ? undefined : "gray",
}}
type={!!params.StatusCode ? "primary" : "link"} size={"small"}
icon={<SearchOutlined/>}
/>
</Popover>
</div>
)
},
cellRender: ({rowData, dataKey, ...props}: any) => {
return (
<div style={{color: StatusCodeToColor(rowData[dataKey])}}>
{rowData[dataKey] === 0 ? "" : rowData[dataKey]}
</div>
)
}
},
{
dataKey: "Url",
resizable: true,
headRender: () => {
return (
<div
style={{display: "flex", justifyContent: "space-between"}}
>
URL
<Popover
placement='bottom'
trigger='click'
content={
params &&
setParams && (
<HTTLFlowFilterDropdownForms
label={"搜索URL关键字"}
params={params}
setParams={setParams}
filterName={"SearchURL"}
pureString={true}
submitFilter={() => update(1)}
/>
)
}
>
<Button
style={{
paddingLeft: 4, paddingRight: 4, marginLeft: 4,
color: !!params.SearchURL ? undefined : "gray",
}}
type={!!params.SearchURL ? "primary" : "link"} size={"small"}
icon={<SearchOutlined/>}
/>
</Popover>
</div>
)
},
cellRender: ({rowData, dataKey, ...props}: any) => {
if (rowData.IsPlaceholder) {
return <div style={{color: "#888585"}}>{"滚轮上滑刷新..."}</div>
}
return (
<div style={{width: "100%", display: "flex"}}>
<div className='resize-ellipsis' title={rowData.Url}>
{!params.SearchURL ? (
rowData.Url
) : (
rowData.Url
)}
</div>
</div>
)
},
width: 600
},
{
dataKey: "HtmlTitle",
width: 120,
resizable: true,
headRender: () => {
return "Title"
},
cellRender: ({rowData, dataKey, ...props}: any) => {
return rowData[dataKey] ? rowData[dataKey] : ""
}
},
{
dataKey: "Tags",
width: 120,
resizable: true,
headRender: () => {
return "Tags"
},
cellRender: ({rowData, dataKey, ...props}: any) => {
return rowData[dataKey] ? (
`${rowData[dataKey]}`.split("|").filter(i => !i.startsWith("YAKIT_COLOR_")).join(", ")
) : ""
}
},
{
dataKey: "IPAddress",
width: 140, resizable: true,
headRender: () => {
return "IP"
},
cellRender: ({rowData, dataKey, ...props}: any) => {
return rowData[dataKey] ? rowData[dataKey] : ""
}
},
{
dataKey: "BodyLength",
width: 120,
sortable: true,
headRender: () => {
return (
<div style={{display: "inline-block", position: "relative"}}>
响应长度
<Popover
placement='bottom'
trigger='click'
content={
params &&
setParams && (
<HTTLFlowFilterDropdownForms
label={"是否存在Body?"}
params={params}
setParams={setParams}
filterName={"HaveBody"}
pureBool={true}
submitFilter={() => update(1)}
/>
)
}
>
<Button
style={{
paddingLeft: 4, paddingRight: 4, marginLeft: 4,
color: !!params.HaveBody ? undefined : "gray",
}}
type={!!params.HaveBody ? "primary" : "link"} size={"small"}
icon={<SearchOutlined/>}
/>
</Popover>
</div>
)
},
cellRender: ({rowData, dataKey, ...props}: any) => {
return (
<div style={{width: 100}}>
{/* 1M 以上的话,是红色*/}
{rowData.BodyLength !== -1 ?
(<div style={{color: rowData.BodyLength > 1000000 ? "red" : undefined}}>
{rowData.BodySizeVerbose
? rowData.BodySizeVerbose
: rowData.BodyLength}
</div>)
:
(<div></div>)
}
</div>
)
}
},
// {
// dataKey: "UrlLength",
// width: 90,
// headRender: () => {
// return "URL 长度"
// },
// cellRender: ({rowData, dataKey, ...props}: any) => {
// const len = (rowData.Url || "").length
// return len > 0 ? <div>{len}</div> : "-"
// }
// },
{
dataKey: "GetParamsTotal",
width: 65,
align: "center",
headRender: () => {
return (
<div
style={{display: "flex", justifyContent: "space-between"}}
>
参数
<Popover
placement='bottom'
trigger='click'
content={
params &&
setParams && (
<HTTLFlowFilterDropdownForms
label={"过滤是否存在基础参数"}
params={params}
setParams={setParams}
filterName={"HaveCommonParams"}
pureBool={true}
submitFilter={() => update(1)}
/>
)
}
>
<Button
style={{
paddingLeft: 4, paddingRight: 4, marginLeft: 4,
color: !!params.HaveCommonParams ? undefined : "gray",
}}
type={!!params.HaveCommonParams ? "primary" : "link"} size={"small"}
icon={<SearchOutlined/>}
/>
</Popover>
</div>
)
},
cellRender: ({rowData, dataKey, ...props}: any) => {
return (
<Space>
{(rowData.GetParamsTotal > 0 ||
rowData.PostParamsTotal > 0) && <CheckOutlined/>}
</Space>
)
}
},
{
dataKey: "ContentType",
resizable: true, width: 80,
headRender: () => {
return "响应类型"
},
cellRender: ({rowData, dataKey, ...props}: any) => {
let contentTypeFixed = rowData.ContentType.split(";")
.map((el: any) => el.trim())
.filter((i: any) => !i.startsWith("charset"))
.join(",") || "-"
if (contentTypeFixed.includes("/")) {
const contentTypeFixedNew = contentTypeFixed.split("/").pop()
if (!!contentTypeFixedNew) {
contentTypeFixed = contentTypeFixedNew
}
}
return (
<div>
{contentTypeFixed === "null" ? "" : contentTypeFixed}
</div>
)
}
},
{
dataKey: "UpdatedAt",
sortable: true,
width: 110,
headRender: () => {
return "请求时间"
},
cellRender: ({rowData, dataKey, ...props}: any) => {
return <Tooltip
title={rowData[dataKey] === 0 ? "" : formatTimestamp(rowData[dataKey])}
>
{rowData[dataKey] === 0 ? "" : formatTime(rowData[dataKey])}
</Tooltip>
}
},
{
dataKey: "operate",
width: 90,
headRender: () => "操作",
cellRender: ({rowData}: any) => {
if (!rowData.Hash) return <></>
return (
<a
onClick={(e) => {
let m = showDrawer({
width: "80%",
content: onExpandHTTPFlow(
rowData,
() => m.destroy()
)
})
}}
>
详情
</a>
)
}
}
]}
data={autoReload ? data : [TableFirstLinePlaceholder].concat(data)}
autoHeight={tableContentHeight <= 0}
height={tableContentHeight}
sortFilter={sortFilter}
renderRow={(children: ReactNode, rowData: any) => {
if (rowData)
return (
<div
id='http-flow-row'
ref={(node) => {
const color =
rowData.Hash === selected?.Hash ?
"rgba(78, 164, 255, 0.4)" :
rowData.Tags.indexOf("YAKIT_COLOR") > -1 ?
TableRowColor(rowData.Tags.split("|").pop().split('_').pop().toUpperCase()) :
"#ffffff"
if (node) {
if (color) node.style.setProperty("background-color", color, "important")
else node.style.setProperty("background-color", "#ffffff")
}
}}
style={{height: "100%"}}
>
{children}
</div>
)
return children
}}
onRowContextMenu={(rowData: HTTPFlow | any, event: React.MouseEvent) => {
if (rowData) {
setSelected(rowData);
}
showByCursorMenu(
{
content: [
{
title: '发送到 Web Fuzzer',
onClick: () => {
ipcRenderer.invoke("send-to-tab", {
type: "fuzzer",
data: {
isHttps: rowData.IsHTTPS,
request: new Buffer(rowData.Request).toString("utf8")
}
})
}
},
{
title: '发送到 数据包扫描',
onClick: () => {
ipcRenderer
.invoke("GetHTTPFlowByHash", {Hash: rowData.Hash})
.then((i: HTTPFlow) => {
ipcRenderer.invoke("send-to-packet-hack", {
request: i.Request,
ishttps: i.IsHTTPS,
response: i.Response
})
})
.catch((e: any) => {
failed(`Query Response failed: ${e}`)
})
}
},
{
title: '复制 URL',
onClick: () => {
callCopyToClipboard(rowData.Url)
},
},
{
title: '复制为 Yak PoC 模版', onClick: () => {
},
subMenuItems: [
{
title: "数据包 PoC 模版", onClick: () => {
const flow = rowData as HTTPFlow;
if (!flow) return;
generateYakCodeByRequest(flow.IsHTTPS, flow.Request, code => {
callCopyToClipboard(code)
}, RequestToYakCodeTemplate.Ordinary)
}
},
{
title: "批量检测 PoC 模版", onClick: () => {
const flow = rowData as HTTPFlow;
if (!flow) return;
generateYakCodeByRequest(flow.IsHTTPS, flow.Request, code => {
callCopyToClipboard(code)
}, RequestToYakCodeTemplate.Batch)
}
},
]
},
{
title: '标注颜色',
subMenuItems: availableColors.map(i => {
return {
title: i.title,
render: i.render,
onClick: () => {
const flow = rowData as HTTPFlow
if (!flow) {
return
}
const existedTags = flow.Tags ? flow.Tags.split("|").filter(i => !!i && !i.startsWith("YAKIT_COLOR_")) : []
existedTags.push(`YAKIT_COLOR_${i.color.toUpperCase()}`)
ipcRenderer.invoke("SetTagForHTTPFlow", {
Id: flow.Id, Hash: flow.Hash,
Tags: existedTags,
}).then(() => {
info(`设置 HTTPFlow 颜色成功`)
if (!autoReload) {
setData(data.map(item => {
if (item.Hash === flow.Hash) {
item.Tags = `YAKIT_COLOR_${i.color.toUpperCase()}`
return item
}
return item
}))
}
})
}
}
}),
onClick: () => {
}
},
{
title: '移除颜色',
onClick: () => {
const flow = rowData as HTTPFlow
if (!flow) return
const existedTags = flow.Tags ? flow.Tags.split("|").filter(i => !!i && !i.startsWith("YAKIT_COLOR_")) : []
existedTags.pop()
ipcRenderer.invoke("SetTagForHTTPFlow", {
Id: flow.Id, Hash: flow.Hash,
Tags: existedTags,
}).then(() => {
info(`清除 HTTPFlow 颜色成功`)
if (!autoReload) {
setData(data.map(item => {
if (item.Hash === flow.Hash) {
item.Tags = ""
return item
}
return item
}))
}
})
return
},
},
{
title: "发送到对比器", onClick: () => {
},
subMenuItems: [
{
title: '发送到对比器左侧',
onClick: () => {
setCompareLeft({
content: new Buffer(rowData.Request).toString("utf8"),
language: 'http'
})
},
disabled: [false, true, false][compareState]
},
{
title: '发送到对比器右侧',
onClick: () => {
setCompareRight({
content: new Buffer(rowData.Request).toString("utf8"),
language: 'http'
})
},
disabled: [false, false, true][compareState]
}
]
},
]
},
event.clientX,
event.clientY
)
}}
onRowClick={(rowDate: any) => {
if (!rowDate.Hash) return
if (rowDate.Hash !== selected?.Hash) {
setSelected(rowDate)
} else {
// setSelected(undefined)
}
}}
onScroll={(scrollX, scrollY) => {
setScrollY(scrollY)
// 防止无数据触发加载
if (data.length === 0 && !getAutoReload()) {
setAutoReload(true)
return
}
// 根据页面展示内容决定是否自动刷新
let contextHeight = (data.length + 1) * ROW_HEIGHT // +1 是要把表 title 算进去
let offsetY = scrollY + tableContentHeight;
if (contextHeight < tableContentHeight) {
setAutoReload(true)
return
}
setAutoReload(false)
// 向下刷新数据
if (contextHeight <= offsetY) {
setAutoReload(false)
scrollUpdateButt(tableContentHeight)
return
}
// 锁住滚轮
if (getLockedScroll() > 0 && getLockedScroll() >= scrollY) {
if (scrollY === getLockedScroll()) {
return
}
// scrollTableTo(getLockedScroll())
return
}
const toTop = scrollY <= 0;
if (toTop) {
lockScrollTimeout(ROW_HEIGHT, 600)
scrollUpdateTop()
}
}}
/>
</div>
// </AutoCard>
)
}