Files
parcer/src/components/organisms/BrowserWindow.tsx

305 lines
12 KiB
TypeScript
Raw Normal View History

2024-06-14 21:47:42 +05:30
import React, { useCallback, useEffect, useState } from 'react';
import { useSocketStore } from '../../context/socket';
2024-08-21 21:57:41 +05:30
import { Button } from '@mui/material';
2024-06-14 21:47:42 +05:30
import Canvas from "../atoms/canvas";
import { useBrowserDimensionsStore } from "../../context/browserDimensions";
2024-07-23 21:58:28 +05:30
import { Highlighter } from "../atoms/Highlighter";
import { GenericModal } from '../atoms/GenericModal';
2024-07-24 20:54:13 +05:30
import { useActionContext } from '../../context/browserActions';
2024-08-09 06:23:10 +05:30
import { useBrowserSteps, TextStep } from '../../context/browserSteps';
2024-07-27 02:06:05 +05:30
interface ElementInfo {
tagName: string;
hasOnlyText?: boolean;
innerText?: string;
url?: string;
imageUrl?: string;
2024-07-27 02:11:59 +05:30
}
2024-08-04 02:54:06 +05:30
interface AttributeOption {
label: string;
value: string;
}
const getAttributeOptions = (tagName: string, elementInfo: ElementInfo | null): AttributeOption[] => {
if (!elementInfo) return [];
2024-08-04 02:54:06 +05:30
switch (tagName.toLowerCase()) {
case 'a':
return [
{ label: `Text: ${elementInfo.innerText || 'N/A'}`, value: 'innerText' },
{ label: `URL: ${elementInfo.url || 'N/A'}`, value: 'href' }
2024-08-04 02:54:06 +05:30
];
case 'img':
return [
{ label: `Alt Text: ${elementInfo.innerText || 'N/A'}`, value: 'alt' },
{ label: `Source URL: ${elementInfo.imageUrl || 'N/A'}`, value: 'src' }
2024-08-04 02:54:06 +05:30
];
default:
return [{ label: `Text: ${elementInfo.innerText || 'N/A'}`, value: 'innerText' }];
2024-08-04 02:54:06 +05:30
}
};
2024-06-14 21:47:42 +05:30
export const BrowserWindow = () => {
const [canvasRef, setCanvasReference] = useState<React.RefObject<HTMLCanvasElement> | undefined>(undefined);
const [screenShot, setScreenShot] = useState<string>("");
2024-07-27 02:11:59 +05:30
const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string, elementInfo: ElementInfo | null; } | null>(null);
2024-08-04 03:18:41 +05:30
const [showAttributeModal, setShowAttributeModal] = useState(false);
const [attributeOptions, setAttributeOptions] = useState<AttributeOption[]>([]);
2024-08-04 03:23:02 +05:30
const [selectedElement, setSelectedElement] = useState<{ selector: string, info: ElementInfo | null } | null>(null);
2024-08-04 03:18:41 +05:30
2024-08-09 06:23:10 +05:30
const [listSelector, setListSelector] = useState<string | null>(null);
const [fields, setFields] = useState<Record<string, TextStep>>({});
2024-06-14 21:47:42 +05:30
const { socket } = useSocketStore();
const { width, height } = useBrowserDimensionsStore();
2024-08-08 00:35:37 +05:30
const { getText, getList } = useActionContext();
2024-08-09 06:23:10 +05:30
const { addTextStep, addListStep } = useBrowserSteps();
2024-06-14 21:47:42 +05:30
2024-07-23 21:58:28 +05:30
const onMouseMove = (e: MouseEvent) => {
if (canvasRef && canvasRef.current && highlighterData) {
const canvasRect = canvasRef.current.getBoundingClientRect();
// mousemove outside the browser window
2024-07-23 21:58:28 +05:30
if (
e.pageX < canvasRect.left
|| e.pageX > canvasRect.right
|| e.pageY < canvasRect.top
|| e.pageY > canvasRect.bottom
) {
setHighlighterData(null);
}
}
};
2024-06-14 21:47:42 +05:30
const screencastHandler = useCallback((data: string) => {
setScreenShot(data);
}, [screenShot]);
2024-06-14 21:47:42 +05:30
2024-06-14 23:17:32 +05:30
useEffect(() => {
2024-06-14 21:47:42 +05:30
if (socket) {
socket.on("screencast", screencastHandler);
}
if (canvasRef?.current) {
drawImage(screenShot, canvasRef.current);
2024-07-23 21:59:20 +05:30
} else {
console.log('Canvas is not initialized');
2024-06-14 21:47:42 +05:30
}
return () => {
socket?.off("screencast", screencastHandler);
}
}, [screenShot, canvasRef, socket, screencastHandler]);
2024-07-27 02:10:10 +05:30
const highlighterHandler = useCallback((data: { rect: DOMRect, selector: string, elementInfo: ElementInfo | null }) => {
2024-08-08 06:29:59 +05:30
if (getList === true) {
socket?.emit('setGetList', { getList: true });
}
2024-06-14 21:47:42 +05:30
setHighlighterData(data);
2024-08-08 06:29:34 +05:30
}, [highlighterData, getList, socket]);
2024-06-14 21:47:42 +05:30
2024-06-14 23:17:32 +05:30
useEffect(() => {
2024-07-23 21:58:28 +05:30
document.addEventListener('mousemove', onMouseMove, false);
2024-06-14 21:47:42 +05:30
if (socket) {
socket.on("highlighter", highlighterHandler);
}
return () => {
2024-07-23 21:58:28 +05:30
document.removeEventListener('mousemove', onMouseMove);
2024-06-14 21:47:42 +05:30
socket?.off("highlighter", highlighterHandler);
};
2024-07-23 21:58:28 +05:30
}, [socket, onMouseMove]);
2024-06-25 22:39:29 +05:30
2024-08-09 08:54:26 +05:30
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (highlighterData && canvasRef?.current) {
const canvasRect = canvasRef.current.getBoundingClientRect();
const clickX = e.clientX - canvasRect.left;
const clickY = e.clientY - canvasRect.top;
2024-08-09 08:54:26 +05:30
const highlightRect = highlighterData.rect;
if (
clickX >= highlightRect.left &&
clickX <= highlightRect.right &&
clickY >= highlightRect.top &&
clickY <= highlightRect.bottom
) {
2024-08-09 00:19:34 +05:30
if (getText === true) {
const options = getAttributeOptions(highlighterData.elementInfo?.tagName || '', highlighterData.elementInfo);
2024-08-06 02:17:28 +05:30
if (options.length > 1) {
setAttributeOptions(options);
setSelectedElement({
selector: highlighterData.selector,
info: highlighterData.elementInfo
});
setShowAttributeModal(true);
} else {
2024-08-06 03:11:28 +05:30
addTextStep('', highlighterData.elementInfo?.innerText || '', {
2024-08-06 02:17:28 +05:30
selector: highlighterData.selector,
tag: highlighterData.elementInfo?.tagName,
attribute: 'innerText'
});
}
2024-08-09 09:25:43 +05:30
}
2024-08-09 08:54:26 +05:30
2024-08-09 06:23:10 +05:30
if (getList === true && !listSelector) {
setListSelector(highlighterData.selector);
} else if (getList === true && listSelector) {
const options = getAttributeOptions(highlighterData.elementInfo?.tagName || '', highlighterData.elementInfo);
2024-08-09 06:23:10 +05:30
if (options.length > 1) {
setAttributeOptions(options);
setSelectedElement({
selector: highlighterData.selector,
info: highlighterData.elementInfo
});
setShowAttributeModal(true);
2024-08-09 09:25:43 +05:30
} else {
2024-08-09 06:23:10 +05:30
const newField: TextStep = {
id: Date.now(),
2024-08-09 09:25:43 +05:30
type: 'text',
2024-08-10 06:25:51 +05:30
label: `Label ${Object.keys(fields).length + 1}`,
2024-08-09 06:23:10 +05:30
data: highlighterData.elementInfo?.innerText || '',
selectorObj: {
selector: highlighterData.selector,
tag: highlighterData.elementInfo?.tagName,
attribute: 'innerText'
}
};
2024-08-09 08:54:26 +05:30
2024-08-09 09:25:43 +05:30
setFields(prevFields => {
const updatedFields = {
...prevFields,
2024-08-10 06:23:44 +05:30
[newField.label]: newField
2024-08-09 09:25:43 +05:30
};
return updatedFields;
});
if (listSelector) {
2024-08-10 06:27:03 +05:30
addListStep(listSelector, { ...fields, [newField.label]: newField });
2024-08-09 09:25:43 +05:30
}
2024-08-09 06:23:10 +05:30
}
2024-08-09 08:54:26 +05:30
2024-08-04 03:18:41 +05:30
}
}
}
};
const handleAttributeSelection = (attribute: string) => {
if (selectedElement) {
let data = '';
switch (attribute) {
case 'href':
data = selectedElement.info?.url || '';
break;
case 'src':
data = selectedElement.info?.imageUrl || '';
break;
default:
data = selectedElement.info?.innerText || '';
}
2024-08-06 02:16:53 +05:30
{
2024-08-09 06:23:10 +05:30
if (getText === true) {
2024-08-06 03:11:28 +05:30
addTextStep('', data, {
2024-08-06 02:16:53 +05:30
selector: selectedElement.selector,
tag: selectedElement.info?.tagName,
attribute: attribute
});
}
if (getList === true) {
const newField: TextStep = {
id: Date.now(),
type: 'text',
label: `Label ${Object.keys(fields).length + 1}`,
data: selectedElement.info?.innerText || '',
selectorObj: {
selector: selectedElement.selector,
tag: selectedElement.info?.tagName,
attribute: attribute
}
};
setFields(prevFields => {
const updatedFields = {
...prevFields,
[newField.label]: newField
};
return updatedFields;
});
if (listSelector) {
addListStep(listSelector, { ...fields, [newField.label]: newField });
}
}
2024-08-06 02:16:53 +05:30
}
}
2024-08-04 03:18:41 +05:30
setShowAttributeModal(false);
};
2024-06-14 21:47:42 +05:30
return (
<div onClick={handleClick}>
{
2024-08-21 22:32:20 +05:30
getText === true || getList === true ? (
<GenericModal
isOpen={showAttributeModal}
onClose={() => { }}
canBeClosed={false}
>
<div>
<h2>Select Attribute</h2>
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
{attributeOptions.map((option) => (
<Button
variant="outlined"
size="medium"
key={option.value}
onClick={() => handleAttributeSelection(option.value)}
style={{
justifyContent: 'flex-start',
2024-08-21 22:37:32 +05:30
maxWidth: '80%',
2024-08-21 22:32:20 +05:30
overflow: 'hidden',
padding: '5px 10px',
}}
>
<span style={{
display: 'block',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '100%'
}}>
{option.label}
</span>
</Button>
))}
</div>
</div>
</GenericModal>
) : null
}
2024-08-08 00:39:28 +05:30
{((getText === true || getList === true) && !showAttributeModal && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ?
2024-07-23 21:58:28 +05:30
<Highlighter
unmodifiedRect={highlighterData?.rect}
displayedSelector={highlighterData?.selector}
width={width}
height={height}
canvasRect={canvasRef.current.getBoundingClientRect()}
/>
: null}
2024-06-14 23:17:32 +05:30
<Canvas
onCreateRef={setCanvasReference}
width={width}
height={height}
2024-06-14 21:47:42 +05:30
/>
2024-07-21 23:51:55 +05:30
</div>
2024-06-14 21:47:42 +05:30
);
};
2024-06-14 23:17:32 +05:30
const drawImage = (image: string, canvas: HTMLCanvasElement): void => {
2024-06-14 21:47:42 +05:30
const ctx = canvas.getContext('2d');
2024-06-14 21:47:42 +05:30
const img = new Image();
2024-06-14 21:47:42 +05:30
img.src = image;
img.onload = () => {
URL.revokeObjectURL(img.src);
2024-07-14 00:46:39 +05:30
ctx?.drawImage(img, 0, 0, 1280, 720);
2024-06-14 21:47:42 +05:30
};
2024-07-14 00:46:39 +05:30
};