From 8e6228d77bd0b777a8848aee7672f7f9b90b392a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 8 Jan 2025 17:22:01 +0530 Subject: [PATCH] fix(temp): revert to old canvas --- src/components/atoms/canvas.tsx | 502 ++++++++++++-------------------- 1 file changed, 186 insertions(+), 316 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 8fd4f791..e71a4d93 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -1,147 +1,21 @@ -import React, { useCallback, useEffect, useRef, useMemo, Suspense } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import { useSocketStore } from '../../context/socket'; +import { getMappedCoordinates } from "../../helpers/inputHelpers"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { useActionContext } from '../../context/browserActions'; -const DatePicker = React.lazy(() => import('./DatePicker')); -const Dropdown = React.lazy(() => import('./Dropdown')); -const TimePicker = React.lazy(() => import('./TimePicker')); -const DateTimeLocalPicker = React.lazy(() => import('./DateTimeLocalPicker')); +import DatePicker from './DatePicker'; +import Dropdown from './Dropdown'; +import TimePicker from './TimePicker'; +import DateTimeLocalPicker from './DateTimeLocalPicker'; -class RAFScheduler { - private queue: Set<() => void> = new Set(); - private isProcessing: boolean = false; - private frameId: number | null = null; - - schedule(callback: () => void): void { - this.queue.add(callback); - if (!this.isProcessing) { - this.process(); - } - } - - private process = (): void => { - this.isProcessing = true; - this.frameId = requestAnimationFrame(() => { - const callbacks = Array.from(this.queue); - this.queue.clear(); - - callbacks.forEach(callback => { - try { - callback(); - } catch (error) { - console.error('RAF Scheduler error:', error); - } - }); - - this.isProcessing = false; - this.frameId = null; - - if (this.queue.size > 0) { - this.process(); - } - }); - } - - clear(): void { - this.queue.clear(); - if (this.frameId !== null) { - cancelAnimationFrame(this.frameId); - this.frameId = null; - } - this.isProcessing = false; - } -} - -class EventDebouncer { - private highPriorityQueue: Array<() => void> = []; - private lowPriorityQueue: Array<() => void> = []; - private processing: boolean = false; - private scheduler: RAFScheduler; - - constructor(scheduler: RAFScheduler) { - this.scheduler = scheduler; - } - - add(callback: () => void, highPriority: boolean = false): void { - if (highPriority) { - this.highPriorityQueue.push(callback); - } else { - this.lowPriorityQueue.push(callback); - } - - if (!this.processing) { - this.process(); - } - } - - private process(): void { - this.processing = true; - this.scheduler.schedule(() => { - while (this.highPriorityQueue.length > 0) { - const callback = this.highPriorityQueue.shift(); - callback?.(); - } - - if (this.lowPriorityQueue.length > 0) { - const callback = this.lowPriorityQueue.shift(); - callback?.(); - - if (this.lowPriorityQueue.length > 0) { - this.process(); - } - } - - this.processing = false; - }); - } - - clear(): void { - this.highPriorityQueue = []; - this.lowPriorityQueue = []; - this.processing = false; - } -} - -// Optimized measurement cache with LRU -class MeasurementCache { - private cache: Map; - private maxSize: number; - - constructor(maxSize: number = 100) { - this.cache = new Map(); - this.maxSize = maxSize; - } - - get(element: HTMLElement): DOMRect | undefined { - const cached = this.cache.get(element); - if (cached) { - // Refresh the entry - this.cache.delete(element); - this.cache.set(element, cached); - } - return cached; - } - - set(element: HTMLElement, rect: DOMRect): void { - if (this.cache.size >= this.maxSize) { - // Remove oldest entry - const firstKey = this.cache.keys().next().value; - if (firstKey !== undefined) { - this.cache.delete(firstKey); - } - } - this.cache.set(element, rect); - } - - clear(): void { - this.cache.clear(); - } +interface CreateRefCallback { + (ref: React.RefObject): void; } interface CanvasProps { width: number; height: number; - onCreateRef: (ref: React.RefObject) => void; + onCreateRef: CreateRefCallback; } /** @@ -152,229 +26,225 @@ export interface Coordinates { y: number; }; -const Canvas = React.memo(({ width, height, onCreateRef }: CanvasProps) => { +const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { + const canvasRef = useRef(null); const { socket } = useSocketStore(); const { setLastAction, lastAction } = useGlobalInfoStore(); const { getText, getList } = useActionContext(); + const getTextRef = useRef(getText); + const getListRef = useRef(getList); - const scheduler = useRef(new RAFScheduler()); - const debouncer = useRef(new EventDebouncer(scheduler.current)); - const measurementCache = useRef(new MeasurementCache(50)); - //const performanceMonitor = useRef(new FrontendPerformanceMonitor()); + const [datePickerInfo, setDatePickerInfo] = React.useState<{ + coordinates: Coordinates; + selector: string; + } | null>(null); - const refs = useRef({ - getText, - getList, - lastMousePosition: { x: 0, y: 0 }, - lastFrameTime: 0, - context: null as CanvasRenderingContext2D | null, - }); + const [dropdownInfo, setDropdownInfo] = React.useState<{ + coordinates: Coordinates; + selector: string; + options: Array<{ + value: string; + text: string; + disabled: boolean; + selected: boolean; + }>; + } | null>(null); - const [state, dispatch] = React.useReducer((state: any, action: any) => { - switch (action.type) { - case 'BATCH_UPDATE': - return { ...state, ...action.payload }; - default: - return state; + const [timePickerInfo, setTimePickerInfo] = React.useState<{ + coordinates: Coordinates; + selector: string; + } | null>(null); + + const [dateTimeLocalInfo, setDateTimeLocalInfo] = React.useState<{ + coordinates: Coordinates; + selector: string; + } | null>(null); + + const notifyLastAction = (action: string) => { + if (lastAction !== action) { + setLastAction(action); } - }, { - datePickerInfo: null, - dropdownInfo: null, - timePickerInfo: null, - dateTimeLocalInfo: null - }); + }; - const getEventCoordinates = useCallback((event: MouseEvent): { x: number; y: number } => { - if (!canvasRef.current) return { x: 0, y: 0 }; + const lastMousePosition = useRef({ x: 0, y: 0 }); - let rect = measurementCache.current.get(canvasRef.current); - if (!rect) { - rect = canvasRef.current.getBoundingClientRect(); - measurementCache.current.set(canvasRef.current, rect); + useEffect(() => { + getTextRef.current = getText; + getListRef.current = getList; + }, [getText, getList]); + + useEffect(() => { + if (socket) { + socket.on('showDatePicker', (info: {coordinates: Coordinates, selector: string}) => { + setDatePickerInfo(info); + }); + + socket.on('showDropdown', (info: { + coordinates: Coordinates, + selector: string, + options: Array<{ + value: string; + text: string; + disabled: boolean; + selected: boolean; + }>; + }) => { + setDropdownInfo(info); + }); + + socket.on('showTimePicker', (info: {coordinates: Coordinates, selector: string}) => { + setTimePickerInfo(info); + }); + + socket.on('showDateTimePicker', (info: {coordinates: Coordinates, selector: string}) => { + setDateTimeLocalInfo(info); + }); + + return () => { + socket.off('showDatePicker'); + socket.off('showDropdown'); + socket.off('showTimePicker'); + socket.off('showDateTimePicker'); + }; } + }, [socket]); - return { - x: event.clientX - rect.left, - y: event.clientY - rect.top - }; - }, []); + const onMouseEvent = useCallback((event: MouseEvent) => { + if (socket && canvasRef.current) { + // Get the canvas bounding rectangle + const rect = canvasRef.current.getBoundingClientRect(); + const clickCoordinates = { + x: event.clientX - rect.left, // Use relative x coordinate + y: event.clientY - rect.top, // Use relative y coordinate + }; - const handleMouseEvent = useCallback((event: MouseEvent) => { - if (!socket || !canvasRef.current) return; - - //performanceMonitor.current.measureEventLatency(event); - const coordinates = getEventCoordinates(event); - - switch (event.type) { - case 'mousedown': - debouncer.current.add(() => { - if (refs.current.getText) { + switch (event.type) { + case 'mousedown': + if (getTextRef.current === true) { console.log('Capturing Text...'); - } else if (refs.current.getList) { + } else if (getListRef.current === true) { console.log('Capturing List...'); } else { - socket.emit('input:mousedown', coordinates); + socket.emit('input:mousedown', clickCoordinates); } - setLastAction('click'); - }, true); // High priority - break; - - case 'mousemove': - if (refs.current.lastMousePosition.x !== coordinates.x || - refs.current.lastMousePosition.y !== coordinates.y) { - debouncer.current.add(() => { - refs.current.lastMousePosition = coordinates; - socket.emit('input:mousemove', coordinates); - setLastAction('move'); - }); - } - break; - - case 'wheel': - const wheelEvent = event as WheelEvent; - debouncer.current.add(() => { - socket.emit('input:wheel', { + notifyLastAction('click'); + break; + case 'mousemove': + if (lastMousePosition.current.x !== clickCoordinates.x || + lastMousePosition.current.y !== clickCoordinates.y) { + lastMousePosition.current = { + x: clickCoordinates.x, + y: clickCoordinates.y, + }; + socket.emit('input:mousemove', { + x: clickCoordinates.x, + y: clickCoordinates.y, + }); + notifyLastAction('move'); + } + break; + case 'wheel': + const wheelEvent = event as WheelEvent; + const deltas = { deltaX: Math.round(wheelEvent.deltaX), - deltaY: Math.round(wheelEvent.deltaY) - }); - setLastAction('scroll'); - }); - break; + deltaY: Math.round(wheelEvent.deltaY), + }; + socket.emit('input:wheel', deltas); + notifyLastAction('scroll'); + break; + default: + console.log('Default mouseEvent registered'); + return; + } } - }, [socket, getEventCoordinates]); + }, [socket]); - const handleKeyboardEvent = useCallback((event: KeyboardEvent) => { - if (!socket) return; - - debouncer.current.add(() => { + const onKeyboardEvent = useCallback((event: KeyboardEvent) => { + if (socket) { switch (event.type) { case 'keydown': - socket.emit('input:keydown', { - key: event.key, - coordinates: refs.current.lastMousePosition - }); - setLastAction(`${event.key} pressed`); + socket.emit('input:keydown', { key: event.key, coordinates: lastMousePosition.current }); + notifyLastAction(`${event.key} pressed`); break; case 'keyup': socket.emit('input:keyup', event.key); break; + default: + console.log('Default keyEvent registered'); + return; } - }, event.type === 'keydown'); // High priority for keydown + } }, [socket]); - // Setup and cleanup - useEffect(() => { - if (!canvasRef.current) return; - - const canvas = canvasRef.current; - refs.current.context = canvas.getContext('2d', { - alpha: false, - desynchronized: true - }); - - onCreateRef(canvasRef); - - const options = { passive: true }; - canvas.addEventListener('mousedown', handleMouseEvent, options); - canvas.addEventListener('mousemove', handleMouseEvent, options); - canvas.addEventListener('wheel', handleMouseEvent, options); - canvas.addEventListener('keydown', handleKeyboardEvent, options); - canvas.addEventListener('keyup', handleKeyboardEvent, options); - - return () => { - canvas.removeEventListener('mousedown', handleMouseEvent); - canvas.removeEventListener('mousemove', handleMouseEvent); - canvas.removeEventListener('wheel', handleMouseEvent); - canvas.removeEventListener('keydown', handleKeyboardEvent); - canvas.removeEventListener('keyup', handleKeyboardEvent); - - scheduler.current.clear(); - debouncer.current.clear(); - measurementCache.current.clear(); - }; - }, [handleMouseEvent, handleKeyboardEvent, onCreateRef]); - - // Performance monitoring - // useEffect(() => { - // const intervalId = setInterval(() => { - // console.log('Performance Report:', performanceMonitor.current.getPerformanceReport()); - // }, 20000); - - // return () => clearInterval(intervalId); - // }, []); useEffect(() => { - if (!socket) return; + if (canvasRef.current) { + onCreateRef(canvasRef); + canvasRef.current.addEventListener('mousedown', onMouseEvent); + canvasRef.current.addEventListener('mousemove', onMouseEvent); + canvasRef.current.addEventListener('wheel', onMouseEvent, { passive: true }); + canvasRef.current.addEventListener('keydown', onKeyboardEvent); + canvasRef.current.addEventListener('keyup', onKeyboardEvent); - const handlers = { - showDatePicker: (info: any) => dispatch({ type: 'BATCH_UPDATE', payload: { datePickerInfo: info } }), - showDropdown: (info: any) => dispatch({ type: 'BATCH_UPDATE', payload: { dropdownInfo: info } }), - showTimePicker: (info: any) => dispatch({ type: 'BATCH_UPDATE', payload: { timePickerInfo: info } }), - showDateTimePicker: (info: any) => dispatch({ type: 'BATCH_UPDATE', payload: { dateTimeLocalInfo: info } }) - }; + return () => { + if (canvasRef.current) { + canvasRef.current.removeEventListener('mousedown', onMouseEvent); + canvasRef.current.removeEventListener('mousemove', onMouseEvent); + canvasRef.current.removeEventListener('wheel', onMouseEvent); + canvasRef.current.removeEventListener('keydown', onKeyboardEvent); + canvasRef.current.removeEventListener('keyup', onKeyboardEvent); + } - Object.entries(handlers).forEach(([event, handler]) => socket.on(event, handler)); - return () => { - Object.keys(handlers).forEach(event => socket.off(event)); - }; - }, [socket]); + }; + } else { + console.log('Canvas not initialized'); + } - const memoizedDimensions = useMemo(() => ({ - width: width || 900, - height: height || 400 - }), [width, height]); + }, [onMouseEvent]); return ( -
+
- - {state.datePickerInfo && ( - dispatch({ - type: 'BATCH_UPDATE', - payload: { datePickerInfo: null } - })} - /> - )} - {state.dropdownInfo && ( - dispatch({ - type: 'BATCH_UPDATE', - payload: { dropdownInfo: null } - })} - /> - )} - {state.timePickerInfo && ( - dispatch({ type: 'SET_TIME_PICKER', payload: null })} - /> - )} - {state.dateTimeLocalInfo && ( - dispatch({ type: 'SET_DATETIME_PICKER', payload: null })} - /> - )} - + {datePickerInfo && ( + setDatePickerInfo(null)} + /> + )} + {dropdownInfo && ( + setDropdownInfo(null)} + /> + )} + {timePickerInfo && ( + setTimePickerInfo(null)} + /> + )} + {dateTimeLocalInfo && ( + setDateTimeLocalInfo(null)} + /> + )}
); -}); -Canvas.displayName = 'Canvas'; +}; + export default Canvas; \ No newline at end of file