import * as THREE from 'three'; import { CameraControls } from '@react-three/drei'; import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; import { useEffect, useRef } from 'react'; import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import useModuleStore from '../../../../store/useModuleStore'; import { getUserData } from '../../../../functions/getUserData'; import { useVersionContext } from '../../version/versionContext'; import { useParams } from 'react-router-dom'; import { useSceneContext } from '../../../scene/sceneContext'; // import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi'; // import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi'; export function useDecalEventHandlers({ parent, decal, visible, }: { parent: Wall | Floor; decal: Decal; visible: boolean; }) { const { wallStore, floorStore } = useSceneContext(); const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById, addDecal: addDecalToWall } = wallStore(); const { removeDecal: removeDecalInFloor, updateDecalPosition: updateDecalPositionInFloor, getFloorById, addDecal: addDecalToFloor } = floorStore(); const { setSelectedWall, setSelectedFloor, setSelectedDecal, setDeletableDecal, deletableDecal, selectedDecal, setDecalDragState, decalDragState } = useBuilderStore(); const { toolMode } = useToolMode(); const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const { socket } = useSocketStore(); const { raycaster, pointer, camera, scene, gl, controls } = useThree(); useFrame(() => { if (activeModule !== 'builder' || toggleView || !decalDragState.isDragging || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return; raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(scene.children, true); const wallIntersect = intersects.find(i => i.object.userData?.wallUuid); const floorIntersect = intersects.find(i => i.object.userData?.floorUuid); let offset = decalDragState.dragOffset || new THREE.Vector3(0, 0, 0); if (wallIntersect) { const wallUuid = wallIntersect.object.userData.wallUuid; const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone()); if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === 'Wall') { updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]); } else if (decal.decalType.type === 'Wall' && wallUuid) { deleteDecal(decal.decalUuid, parent); const addedDecal = addDecalToWall(wallUuid, { ...decal, decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]], decalType: { type: 'Wall', wallUuid: wallUuid } }); if (addedDecal) { setSelectedDecal({ decalMesh: null, decalData: addedDecal }) } } else if (decal.decalType.type === 'Floor' && wallUuid) { deleteDecal(decal.decalUuid, parent); const wall = getWallById(wallUuid); if (!wall) return; const addedDecal = addDecalToWall(wallUuid, { ...decal, decalPosition: [point.x + offset.x, point.y + offset.y, wall.wallThickness / 2 + 0.001], decalType: { type: 'Wall', wallUuid: wallUuid } }); if (addedDecal) { setSelectedDecal({ decalMesh: null, decalData: addedDecal }) } } } else if (floorIntersect) { const floorUuid = floorIntersect.object.userData.floorUuid; const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone()); if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === 'Floor') { updateDecalPositionInFloor(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]); } else if (decal.decalType.type === 'Floor' && floorUuid) { deleteDecal(decal.decalUuid, parent); const addedDecal = addDecalToFloor(floorUuid, { ...decal, decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]], decalType: { type: 'Floor', floorUuid: floorUuid } }); if (addedDecal) { setSelectedDecal({ decalMesh: null, decalData: addedDecal }) } } else if (decal.decalType.type === 'Wall' && floorUuid) { deleteDecal(decal.decalUuid, parent); const floor = getFloorById(floorUuid); if (!floor) return; const addedDecal = addDecalToFloor(floorUuid, { ...decal, decalPosition: [point.x + offset.x, point.y + offset.y, -0.001], decalType: { type: 'Floor', floorUuid: floorUuid } }); if (addedDecal) { setSelectedDecal({ decalMesh: null, decalData: addedDecal }) } } } }); const handlePointerUp = (e: PointerEvent) => { if (controls) { (controls as CameraControls).enabled = true; } if (decalDragState.isDragging) { setDecalDragState(false, null, null); if ('wallUuid' in parent) { setTimeout(() => { const updatedWall = getWallById(parent.wallUuid); if (updatedWall) { if (projectId && updatedWall) { // API // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); // SOCKET const data = { wallData: updatedWall, projectId: projectId, versionId: selectedVersion?.versionId || '', userId: userId, organization: organization } socket.emit('v1:model-Wall:add', data); } } }, 0) } else if ('floorUuid' in parent) { setTimeout(() => { const updatedFloor = parent; if (projectId && updatedFloor) { // API // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); // SOCKET const data = { floorData: updatedFloor, projectId: projectId, versionId: selectedVersion?.versionId || '', userId: userId, organization: organization } socket.emit('v1:model-Floor:add', data); } }, 0) } } }; const deleteDecal = (decalUuid: string, parent: Wall | Floor) => { if ('wallUuid' in parent) { const updatedWall = removeDecalInWall(decalUuid); if (projectId && updatedWall) { // API // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); // SOCKET const data = { wallData: updatedWall, projectId: projectId, versionId: selectedVersion?.versionId || '', userId: userId, organization: organization } socket.emit('v1:model-Wall:add', data); } } else if ('floorUuid' in parent) { const updatedFloor = removeDecalInFloor(decalUuid); if (projectId && updatedFloor) { // API // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); // SOCKET const data = { floorData: updatedFloor, projectId: projectId, versionId: selectedVersion?.versionId || '', userId: userId, organization: organization } socket.emit('v1:model-Floor:add', data); } } } const handlePointerDown = (e: ThreeEvent) => { if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.decalUuid && toolMode === 'cursor') { e.stopPropagation(); setDecalDragState(true, decal.decalUuid, null); if (controls) { (controls as CameraControls).enabled = false; } setSelectedDecal({ decalMesh: e.object, decalData: decal }); setSelectedWall(null); setSelectedFloor(null); const localIntersect = e.object.worldToLocal(e.point.clone()); let dragOffset = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0); setDecalDragState(true, decal.decalUuid, dragOffset); } } }; const handleClick = (e: ThreeEvent) => { if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.decalUuid) { e.stopPropagation(); if (toolMode === 'cursor') { setSelectedDecal({ decalMesh: e.object, decalData: decal }); setSelectedWall(null); setSelectedFloor(null); } else if (toolMode === '3D-Delete') { deleteDecal(e.object.userData.decalUuid, parent); } } } }; const handlePointerEnter = (e: ThreeEvent) => { if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.decalUuid) { e.stopPropagation(); if (toolMode === '3D-Delete') { setDeletableDecal(e.object); } } } }; const handlePointerLeave = (e: ThreeEvent) => { if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.decalUuid) { e.stopPropagation(); if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) { setDeletableDecal(null); } } } }; const handlePointerMissed = () => { if (selectedDecal && selectedDecal.decalMesh && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) { setSelectedDecal(null); } }; useEffect(() => { const canvasElement = gl.domElement; if (activeModule === 'builder' && !toggleView && selectedDecal && selectedDecal.decalData.decalUuid === decal.decalUuid) { canvasElement.addEventListener('pointerup', handlePointerUp); } return () => { canvasElement.removeEventListener('pointerup', handlePointerUp); }; }, [gl, activeModule, toggleView, selectedDecal, camera, controls, visible, parent, decal, decalDragState]); return { handlePointerDown, handleClick, handlePointerEnter, handlePointerLeave, handlePointerMissed, deleteDecal }; }