feat: rm canvas logic, add in-browser loader
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
|||||||
export const BrowserContent = () => {
|
export const BrowserContent = () => {
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
|
|
||||||
const [tabs, setTabs] = useState<string[]>(["current"]);
|
const [tabs, setTabs] = useState<string[]>(["Loading..."]);
|
||||||
const [tabIndex, setTabIndex] = React.useState(0);
|
const [tabIndex, setTabIndex] = React.useState(0);
|
||||||
const [showOutputData, setShowOutputData] = useState(false);
|
const [showOutputData, setShowOutputData] = useState(false);
|
||||||
const { browserWidth } = useBrowserDimensionsStore();
|
const { browserWidth } = useBrowserDimensionsStore();
|
||||||
@@ -125,7 +125,7 @@ export const BrowserContent = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCurrentTabs()
|
getCurrentTabs()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response) {
|
if (response && response.length > 0) {
|
||||||
setTabs(response);
|
setTabs(response);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import { useSocketStore } from '../../context/socket';
|
import { useSocketStore } from '../../context/socket';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import Canvas from "../recorder/Canvas";
|
|
||||||
import { Highlighter } from "../recorder/Highlighter";
|
|
||||||
import { GenericModal } from '../ui/GenericModal';
|
import { GenericModal } from '../ui/GenericModal';
|
||||||
import { useActionContext } from '../../context/browserActions';
|
import { useActionContext } from '../../context/browserActions';
|
||||||
import { useBrowserSteps, TextStep, ListStep } from '../../context/browserSteps';
|
import { useBrowserSteps, TextStep, ListStep } from '../../context/browserSteps';
|
||||||
@@ -38,12 +36,6 @@ interface AttributeOption {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScreencastData {
|
|
||||||
image: string;
|
|
||||||
userId: string;
|
|
||||||
viewport?: ViewportInfo | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ViewportInfo {
|
interface ViewportInfo {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
@@ -146,8 +138,6 @@ const getAttributeOptions = (tagName: string, elementInfo: ElementInfo | null):
|
|||||||
export const BrowserWindow = () => {
|
export const BrowserWindow = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { browserWidth, browserHeight } = useBrowserDimensionsStore();
|
const { browserWidth, browserHeight } = useBrowserDimensionsStore();
|
||||||
const [canvasRef, setCanvasReference] = useState<React.RefObject<HTMLCanvasElement> | undefined>(undefined);
|
|
||||||
const [screenShot, setScreenShot] = useState<string>("");
|
|
||||||
const [highlighterData, setHighlighterData] = useState<{
|
const [highlighterData, setHighlighterData] = useState<{
|
||||||
rect: DOMRect;
|
rect: DOMRect;
|
||||||
selector: string;
|
selector: string;
|
||||||
@@ -1303,17 +1293,6 @@ export const BrowserWindow = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onMouseMove = (e: MouseEvent) => {
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
if (canvasRef && canvasRef.current && highlighterData) {
|
|
||||||
const canvasRect = canvasRef.current.getBoundingClientRect();
|
|
||||||
if (
|
|
||||||
e.pageX < canvasRect.left
|
|
||||||
|| e.pageX > canvasRect.right
|
|
||||||
|| e.pageY < canvasRect.top
|
|
||||||
|| e.pageY > canvasRect.bottom
|
|
||||||
) {
|
|
||||||
setHighlighterData(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetListState = useCallback(() => {
|
const resetListState = useCallback(() => {
|
||||||
@@ -1331,35 +1310,15 @@ export const BrowserWindow = () => {
|
|||||||
}
|
}
|
||||||
}, [getList, resetListState]);
|
}, [getList, resetListState]);
|
||||||
|
|
||||||
const screencastHandler = useCallback((data: string | ScreencastData) => {
|
|
||||||
if (typeof data === 'string') {
|
|
||||||
setScreenShot(data);
|
|
||||||
} else if (data && typeof data === 'object' && 'image' in data) {
|
|
||||||
if (!data.userId || data.userId === user?.id) {
|
|
||||||
setScreenShot(data.image);
|
|
||||||
|
|
||||||
if (data.viewport) {
|
|
||||||
setViewportInfo(data.viewport);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [user?.id]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.on("screencast", screencastHandler);
|
|
||||||
socket.on("domcast", rrwebSnapshotHandler);
|
socket.on("domcast", rrwebSnapshotHandler);
|
||||||
socket.on("dom-mode-enabled", domModeHandler);
|
socket.on("dom-mode-enabled", domModeHandler);
|
||||||
socket.on("dom-mode-error", domModeErrorHandler);
|
socket.on("dom-mode-error", domModeErrorHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canvasRef?.current && !isDOMMode && screenShot) {
|
|
||||||
drawImage(screenShot, canvasRef.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.off("screencast", screencastHandler);
|
|
||||||
socket.off("domcast", rrwebSnapshotHandler);
|
socket.off("domcast", rrwebSnapshotHandler);
|
||||||
socket.off("dom-mode-enabled", domModeHandler);
|
socket.off("dom-mode-enabled", domModeHandler);
|
||||||
socket.off("dom-mode-error", domModeErrorHandler);
|
socket.off("dom-mode-error", domModeErrorHandler);
|
||||||
@@ -1367,10 +1326,6 @@ export const BrowserWindow = () => {
|
|||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
socket,
|
socket,
|
||||||
screenShot,
|
|
||||||
canvasRef,
|
|
||||||
isDOMMode,
|
|
||||||
screencastHandler,
|
|
||||||
rrwebSnapshotHandler,
|
rrwebSnapshotHandler,
|
||||||
domModeHandler,
|
domModeHandler,
|
||||||
domModeErrorHandler,
|
domModeErrorHandler,
|
||||||
@@ -1847,24 +1802,7 @@ export const BrowserWindow = () => {
|
|||||||
|
|
||||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (highlighterData) {
|
if (highlighterData) {
|
||||||
let shouldProcessClick = false;
|
const shouldProcessClick = true;
|
||||||
|
|
||||||
if (!isDOMMode && canvasRef?.current) {
|
|
||||||
const canvasRect = canvasRef.current.getBoundingClientRect();
|
|
||||||
const clickX = e.clientX - canvasRect.left;
|
|
||||||
const clickY = e.clientY - canvasRect.top;
|
|
||||||
const highlightRect = highlighterData.rect;
|
|
||||||
const mappedRect =
|
|
||||||
coordinateMapper.mapBrowserRectToCanvas(highlightRect);
|
|
||||||
|
|
||||||
shouldProcessClick =
|
|
||||||
clickX >= mappedRect.left &&
|
|
||||||
clickX <= mappedRect.right &&
|
|
||||||
clickY >= mappedRect.top &&
|
|
||||||
clickY <= mappedRect.bottom;
|
|
||||||
} else {
|
|
||||||
shouldProcessClick = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldProcessClick) {
|
if (shouldProcessClick) {
|
||||||
const options = getAttributeOptions(
|
const options = getAttributeOptions(
|
||||||
@@ -2209,17 +2147,7 @@ export const BrowserWindow = () => {
|
|||||||
!showAttributeModal &&
|
!showAttributeModal &&
|
||||||
highlighterData?.rect != null && (
|
highlighterData?.rect != null && (
|
||||||
<>
|
<>
|
||||||
{!isDOMMode && canvasRef?.current && (
|
{highlighterData && (
|
||||||
<Highlighter
|
|
||||||
unmodifiedRect={highlighterData?.rect}
|
|
||||||
displayedSelector={highlighterData?.selector}
|
|
||||||
width={dimensions.width}
|
|
||||||
height={dimensions.height}
|
|
||||||
canvasRect={canvasRef.current.getBoundingClientRect()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isDOMMode && highlighterData && (
|
|
||||||
<div
|
<div
|
||||||
id="dom-highlight-overlay"
|
id="dom-highlight-overlay"
|
||||||
style={{
|
style={{
|
||||||
@@ -2355,31 +2283,27 @@ export const BrowserWindow = () => {
|
|||||||
borderRadius: "0px 0px 5px 5px",
|
borderRadius: "0px 0px 5px 5px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isDOMMode ? (
|
{currentSnapshot ? (
|
||||||
<>
|
<>
|
||||||
{currentSnapshot ? (
|
<DOMBrowserRenderer
|
||||||
<DOMBrowserRenderer
|
width={dimensions.width}
|
||||||
width={dimensions.width}
|
height={dimensions.height}
|
||||||
height={dimensions.height}
|
snapshot={currentSnapshot}
|
||||||
snapshot={currentSnapshot}
|
getList={getList}
|
||||||
getList={getList}
|
getText={getText}
|
||||||
getText={getText}
|
listSelector={listSelector}
|
||||||
listSelector={listSelector}
|
cachedChildSelectors={cachedChildSelectors}
|
||||||
cachedChildSelectors={cachedChildSelectors}
|
paginationMode={paginationMode}
|
||||||
paginationMode={paginationMode}
|
paginationType={paginationType}
|
||||||
paginationType={paginationType}
|
limitMode={limitMode}
|
||||||
limitMode={limitMode}
|
isCachingChildSelectors={isCachingChildSelectors}
|
||||||
isCachingChildSelectors={isCachingChildSelectors}
|
onHighlight={domHighlighterHandler}
|
||||||
onHighlight={domHighlighterHandler}
|
onElementSelect={handleDOMElementSelection}
|
||||||
onElementSelect={handleDOMElementSelection}
|
onShowDatePicker={handleShowDatePicker}
|
||||||
onShowDatePicker={handleShowDatePicker}
|
onShowDropdown={handleShowDropdown}
|
||||||
onShowDropdown={handleShowDropdown}
|
onShowTimePicker={handleShowTimePicker}
|
||||||
onShowTimePicker={handleShowTimePicker}
|
onShowDateTimePicker={handleShowDateTimePicker}
|
||||||
onShowDateTimePicker={handleShowDateTimePicker}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<DOMLoadingIndicator />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* --- Loading overlay --- */}
|
{/* --- Loading overlay --- */}
|
||||||
{isCachingChildSelectors && (
|
{isCachingChildSelectors && (
|
||||||
@@ -2492,11 +2416,7 @@ export const BrowserWindow = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Canvas
|
<DOMLoadingIndicator />
|
||||||
onCreateRef={setCanvasReference}
|
|
||||||
width={dimensions.width}
|
|
||||||
height={dimensions.height}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2591,26 +2511,6 @@ const DOMLoadingIndicator: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const drawImage = (image: string, canvas: HTMLCanvasElement): void => {
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
if (!ctx) return;
|
|
||||||
|
|
||||||
const img = new Image();
|
|
||||||
img.onload = () => {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
||||||
});
|
|
||||||
if (image.startsWith('blob:')) {
|
|
||||||
URL.revokeObjectURL(image);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
img.onerror = () => {
|
|
||||||
console.warn('Failed to load image');
|
|
||||||
};
|
|
||||||
img.src = image;
|
|
||||||
};
|
|
||||||
|
|
||||||
const modalStyle = {
|
const modalStyle = {
|
||||||
top: '50%',
|
top: '50%',
|
||||||
left: '50%',
|
left: '50%',
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
|||||||
|
|
||||||
const { setId, socket } = useSocketStore();
|
const { setId, socket } = useSocketStore();
|
||||||
const { setWidth } = useBrowserDimensionsStore();
|
const { setWidth } = useBrowserDimensionsStore();
|
||||||
const { browserId, setBrowserId, recordingId, recordingUrl, setRecordingUrl, setRecordingName, setRetrainRobotId } = useGlobalInfoStore();
|
const { browserId, setBrowserId, recordingId, recordingUrl, setRecordingUrl, setRecordingName, setRetrainRobotId, setIsDOMMode } = useGlobalInfoStore();
|
||||||
|
|
||||||
const handleShowOutputData = useCallback(() => {
|
const handleShowOutputData = useCallback(() => {
|
||||||
setShowOutputData(true);
|
setShowOutputData(true);
|
||||||
@@ -77,6 +77,8 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isCancelled = false;
|
let isCancelled = false;
|
||||||
const handleRecording = async () => {
|
const handleRecording = async () => {
|
||||||
|
setIsDOMMode(true);
|
||||||
|
|
||||||
const storedUrl = window.sessionStorage.getItem('recordingUrl');
|
const storedUrl = window.sessionStorage.getItem('recordingUrl');
|
||||||
if (storedUrl && !recordingUrl) {
|
if (storedUrl && !recordingUrl) {
|
||||||
setRecordingUrl(storedUrl);
|
setRecordingUrl(storedUrl);
|
||||||
@@ -137,9 +139,12 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
|||||||
if (browserId === 'new-recording') {
|
if (browserId === 'new-recording') {
|
||||||
socket?.emit('new-recording');
|
socket?.emit('new-recording');
|
||||||
}
|
}
|
||||||
|
if (recordingUrl && socket) {
|
||||||
|
socket.emit('input:url', recordingUrl);
|
||||||
|
}
|
||||||
setIsLoaded(true);
|
setIsLoaded(true);
|
||||||
}
|
}
|
||||||
}, [socket, browserId, recordingName, recordingId, isLoaded]);
|
}, [socket, browserId, recordingName, recordingId, recordingUrl, isLoaded]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket?.on('loaded', handleLoaded);
|
socket?.on('loaded', handleLoaded);
|
||||||
@@ -153,26 +158,20 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
|||||||
<ActionProvider>
|
<ActionProvider>
|
||||||
<BrowserStepsProvider>
|
<BrowserStepsProvider>
|
||||||
<div id="browser-recorder">
|
<div id="browser-recorder">
|
||||||
{isLoaded ? (
|
<Grid container direction="row" style={{ flexGrow: 1, height: '100%' }}>
|
||||||
<>
|
<Grid item xs={12} md={9} lg={9} style={{ height: '100%', overflow: 'hidden', position: 'relative' }}>
|
||||||
<Grid container direction="row" style={{ flexGrow: 1, height: '100%' }}>
|
<div style={{ height: '100%', overflow: 'auto' }}>
|
||||||
<Grid item xs={12} md={9} lg={9} style={{ height: '100%', overflow: 'hidden', position: 'relative' }}>
|
<BrowserContent />
|
||||||
<div style={{ height: '100%', overflow: 'auto' }}>
|
<InterpretationLog isOpen={showOutputData} setIsOpen={setShowOutputData} />
|
||||||
<BrowserContent />
|
</div>
|
||||||
<InterpretationLog isOpen={showOutputData} setIsOpen={setShowOutputData} />
|
</Grid>
|
||||||
</div>
|
<Grid item xs={12} md={3} lg={3} style={{ height: '100%', overflow: 'hidden' }}>
|
||||||
</Grid>
|
<div className="right-side-panel" style={{ height: '100%' }}>
|
||||||
<Grid item xs={12} md={3} lg={3} style={{ height: '100%', overflow: 'hidden' }}>
|
<RightSidePanel onFinishCapture={handleShowOutputData} />
|
||||||
<div className="right-side-panel" style={{ height: '100%' }}>
|
<BrowserRecordingSave />
|
||||||
<RightSidePanel onFinishCapture={handleShowOutputData} />
|
</div>
|
||||||
<BrowserRecordingSave />
|
</Grid>
|
||||||
</div>
|
</Grid>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Loader text={t('recording_page.loader.browser_startup', { url: recordingUrl })} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</BrowserStepsProvider>
|
</BrowserStepsProvider>
|
||||||
</ActionProvider>
|
</ActionProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user