diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/eventHandler/useWallAssetEventHandler.ts b/app/src/modules/builder/wallAsset/Instances/Instance/eventHandler/useWallAssetEventHandler.ts new file mode 100644 index 0000000..1fbce4c --- /dev/null +++ b/app/src/modules/builder/wallAsset/Instances/Instance/eventHandler/useWallAssetEventHandler.ts @@ -0,0 +1,213 @@ +import * as THREE from 'three'; +import { useCallback, useEffect, useRef } from 'react'; +import { ThreeEvent, useThree } from '@react-three/fiber'; +import { useParams } from 'react-router-dom'; +import { useActiveTool, useSocketStore, useToggleView } from '../../../../../../store/builder/store'; +import useModuleStore from '../../../../../../store/useModuleStore'; +import { useSceneContext } from '../../../../../scene/sceneContext'; +import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore'; +import { useVersionContext } from '../../../../version/versionContext'; +import { getUserData } from '../../../../../../functions/getUserData'; +import closestPointOnLineSegment from '../../../../line/helpers/getClosestPointOnLineSegment'; +import { upsertWallAssetApi } from '../../../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi'; +import { deleteWallAssetApi } from '../../../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi'; + +export function useWallAssetEventHandler({ + wallAsset, + groupRef, + wall, +}: { + wallAsset: WallAsset; + groupRef: React.RefObject; + wall: Wall | undefined; +}) { + const { socket } = useSocketStore(); + const { raycaster, pointer, camera, scene, controls, gl } = useThree(); + const { wallAssetStore } = useSceneContext(); + const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore(); + const { toggleView } = useToggleView(); + const { activeTool } = useActiveTool(); + const { activeModule } = useModuleStore(); + const { selectedWallAsset, setSelectedWallAsset, setDeletableWallAsset, deletableWallAsset } = useBuilderStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + + const draggingRef = useRef(false); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onPointerUp = (e: PointerEvent) => { + if (draggingRef.current) { + draggingRef.current = false; + if (controls) { + (controls as any).enabled = true; + } + + if (selectedWallAsset) { + const updatedWallAsset = getWallAssetById(wallAsset.modelUuid); + + if (projectId && updatedWallAsset) { + if (!socket?.connected) { + + // API + + upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset); + } else { + + // SOCKET + + const data = { + wallAssetData: updatedWallAsset, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + socket.emit('v1:wall-asset:add', data); + } + } + } + } + }; + + const onPointerMove = (e: any) => { + if (!draggingRef.current || !wall || !selectedWallAsset) return; + if (controls) { + (controls as any).enabled = false; + } + + pointer.x = (e.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(e.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true); + const intersect = intersects.find((i: any) => i.object.name.includes('WallReference')); + + if (intersect?.object.userData.wallUuid) { + const newPoint = closestPointOnLineSegment( + new THREE.Vector3(intersect.point.x, 0, intersect.point.z), + new THREE.Vector3(...intersect.object.userData.points[0].position), + new THREE.Vector3(...intersect.object.userData.points[1].position) + ); + + const wallRotation = intersect.object.rotation.clone(); + + updateWallAsset(wallAsset.modelUuid, { + wallUuid: intersect.object.userData.wallUuid, + position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z], + rotation: [wallRotation.x, wallRotation.y, wallRotation.z], + }); + } + }; + + if (selectedWallAsset && !toggleView && activeModule === 'builder') { + canvasElement.addEventListener('mousemove', onPointerMove); + canvasElement.addEventListener('pointerup', onPointerUp); + } + + return () => { + canvasElement.removeEventListener('mousemove', onPointerMove); + canvasElement.removeEventListener('pointerup', onPointerUp); + }; + }, [gl, camera, toggleView, activeModule, selectedWallAsset, socket, wall, wallAsset]); + + const handlePointerEnter = useCallback((evt: ThreeEvent) => { + if (!toggleView) { + evt.stopPropagation(); + let currentObject = evt.object; + while (currentObject) { + if (currentObject.userData.wallUuid) { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + if (activeTool === "delete" && activeModule === 'builder') { + if (deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { + return; + } else { + setDeletableWallAsset(currentObject); + } + } + } + }, [activeTool, activeModule, deletableWallAsset, toggleView]); + + const handlePointerLeave = useCallback((evt: ThreeEvent) => { + if (!toggleView) { + evt.stopPropagation(); + if (evt.intersections.length === 0 && activeTool === "delete" && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { + setDeletableWallAsset(null); + } + } + }, [activeTool, deletableWallAsset, toggleView]); + + const handlePointerDown = useCallback((e: ThreeEvent) => { + if (!toggleView && activeModule === 'builder' && selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { + draggingRef.current = true; + e.stopPropagation(); + setSelectedWallAsset(groupRef.current); + if (controls) { + (controls as any).enabled = false; + } + } + }, [toggleView, activeModule, selectedWallAsset, wallAsset]); + + const handleClick = useCallback((e: ThreeEvent) => { + if (!toggleView && activeModule === 'builder' && activeTool !== 'delete') { + if (e.object) { + e.stopPropagation(); + let currentObject = e.object; + while (currentObject) { + if (currentObject.userData.wallUuid) { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + setSelectedWallAsset(currentObject); + } + } else if (!toggleView && activeModule === 'builder' && activeTool === 'delete') { + if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { + const removedWallAsset = removeWallAsset(wallAsset.modelUuid); + + if (projectId && removedWallAsset) { + if (!socket?.connected) { + + // API + + deleteWallAssetApi(projectId, selectedVersion?.versionId || '', removedWallAsset.modelUuid, removedWallAsset.wallUuid); + } else { + + // SOCKET + + const data = { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + modelUuid: removedWallAsset.modelUuid, + wallUuid: removedWallAsset.wallUuid + } + socket.emit('v1:wall-asset:delete', data); + } + } + } + } + }, [toggleView, activeModule, activeTool, wallAsset, deletableWallAsset, socket]); + + const handlePointerMissed = useCallback(() => { + if (selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { + setSelectedWallAsset(null); + } + }, [selectedWallAsset, wallAsset]); + + return { + handlePointerDown, + handleClick, + handlePointerMissed, + handlePointerEnter, + handlePointerLeave, + draggingRef + }; +} \ No newline at end of file diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx index 73503ff..7d88a96 100644 --- a/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx +++ b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx @@ -1,42 +1,22 @@ import * as THREE from 'three'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { ThreeEvent, useThree } from '@react-three/fiber'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { Base, Geometry, Subtraction } from '@react-three/csg'; -import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; -import useModuleStore from '../../../../../store/useModuleStore'; +import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; import { useSceneContext } from '../../../../scene/sceneContext'; -import { useBuilderStore } from '../../../../../store/builder/useBuilderStore'; -import { useActiveTool, useSocketStore, useToggleView } from '../../../../../store/builder/store'; -import { useParams } from 'react-router-dom'; -import { useVersionContext } from '../../../version/versionContext'; -import { getUserData } from '../../../../../functions/getUserData'; -import closestPointOnLineSegment from '../../../line/helpers/getClosestPointOnLineSegment'; -import { upsertWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi'; -import { deleteWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi'; +import { useWallAssetEventHandler } from './eventHandler/useWallAssetEventHandler'; function WallAssetInstance({ wallAsset }: { readonly wallAsset: WallAsset }) { const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - const { socket } = useSocketStore(); - const { raycaster, pointer, camera, scene, controls, gl } = useThree(); - const { wallStore, wallAssetStore } = useSceneContext(); + const { wallStore } = useSceneContext(); const { walls, getWallById } = wallStore(); - const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore(); - const { toggleView } = useToggleView(); - const { activeTool } = useActiveTool(); - const { activeModule } = useModuleStore(); - const { selectedWallAsset, setSelectedWallAsset, setDeletableWallAsset, deletableWallAsset } = useBuilderStore(); const [gltfScene, setGltfScene] = useState(null); const [boundingBox, setBoundingBox] = useState(null); const groupRef = useRef(null); const wall = useMemo(() => getWallById(wallAsset.wallUuid), [getWallById, wallAsset.wallUuid, walls]); - const draggingRef = useRef(false); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { userId, organization } = getUserData(); - const { projectId } = useParams(); + const { handlePointerDown, handleClick, handlePointerMissed, handlePointerEnter, handlePointerLeave } = useWallAssetEventHandler({ wallAsset, groupRef, wall }); useEffect(() => { const loader = new GLTFLoader(); @@ -112,130 +92,6 @@ function WallAssetInstance({ wallAsset }: { readonly wallAsset: WallAsset }) { }, []); - useEffect(() => { - const canvasElement = gl.domElement; - - const onPointerUp = (e: PointerEvent) => { - if (draggingRef.current) { - draggingRef.current = false; - if (controls) { - (controls as any).enabled = true; - } - - if (selectedWallAsset) { - const updatedWallAsset = getWallAssetById(wallAsset.modelUuid); - - if (projectId && updatedWallAsset) { - if (!socket?.connected) { - - // API - - upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset); - } else { - - // SOCKET - - const data = { - wallAssetData: updatedWallAsset, - projectId: projectId, - versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization - } - - socket.emit('v1:wall-asset:add', data); - } - } - } - } - }; - - const onPointerMove = (e: any) => { - if (!draggingRef.current || !wall || !selectedWallAsset) return; - if (controls) { - (controls as any).enabled = false; - } - pointer.x = (e.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(e.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true); - const intersect = intersects.find((i: any) => i.object.name.includes('WallReference')); - - if (intersect?.object.userData.wallUuid) { - const newPoint = closestPointOnLineSegment( - new THREE.Vector3(intersect.point.x, 0, intersect.point.z), - new THREE.Vector3(...intersect.object.userData.points[0].position), - new THREE.Vector3(...intersect.object.userData.points[1].position) - ); - - const wallRotation = intersect.object.rotation.clone(); - - updateWallAsset(wallAsset.modelUuid, { - wallUuid: intersect.object.userData.wallUuid, - position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z], - rotation: [wallRotation.x, wallRotation.y, wallRotation.z], - }); - } - }; - - if (selectedWallAsset && !toggleView && activeModule === 'builder') { - canvasElement.addEventListener('mousemove', onPointerMove); - canvasElement.addEventListener('pointerup', onPointerUp); - } - - return () => { - canvasElement.removeEventListener('mousemove', onPointerMove); - canvasElement.removeEventListener('pointerup', onPointerUp); - }; - - }, [gl, camera, toggleView, activeModule, selectedWallAsset, socket]) - - const handlePointerClick = useCallback((wallAsset: WallAsset) => { - if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { - const removedWallAsset = removeWallAsset(wallAsset.modelUuid); - - if (projectId && removedWallAsset) { - if (!socket?.connected) { - - // API - - deleteWallAssetApi(projectId, selectedVersion?.versionId || '', removedWallAsset.modelUuid, removedWallAsset.wallUuid); - } else { - - // SOCKET - - const data = { - projectId: projectId, - versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization, - modelUuid: removedWallAsset.modelUuid, - wallUuid: removedWallAsset.wallUuid - } - - socket.emit('v1:wall-asset:delete', data); - } - } - } - }, [activeTool, activeModule, deletableWallAsset, socket]) - - const handlePointerOver = useCallback((wallAsset: WallAsset, currentObject: THREE.Object3D) => { - if (activeTool === "delete" && activeModule === 'builder') { - if (deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { - return; - } else { - setDeletableWallAsset(currentObject); - } - } - }, [activeTool, activeModule, deletableWallAsset]); - - const handlePointerOut = useCallback((evt: ThreeEvent, wallAsset: WallAsset) => { - if (evt.intersections.length === 0 && activeTool === "delete" && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { - setDeletableWallAsset(null); - } - }, [activeTool, deletableWallAsset]); - if (!gltfScene || !boundingBox) { return null } const size = new THREE.Vector3(); boundingBox.getSize(size); @@ -259,57 +115,11 @@ function WallAssetInstance({ wallAsset }: { readonly wallAsset: WallAsset }) { {gltfScene && ( { - if (!toggleView && activeModule === 'builder' && selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { - draggingRef.current = true; - e.stopPropagation(); - setSelectedWallAsset(groupRef.current); - if (controls) { - (controls as any).enabled = false; - } - } - }} - onClick={(e) => { - if (!toggleView && activeModule === 'builder' && activeTool !== 'delete') { - if (e.object) { - e.stopPropagation(); - let currentObject = e.object as THREE.Object3D; - while (currentObject) { - if (currentObject.userData.wallUuid) { - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - setSelectedWallAsset(currentObject); - } - } else if (!toggleView && activeModule === 'builder' && activeTool === 'delete') { - handlePointerClick(wallAsset); - } - }} - onPointerMissed={() => { - if (selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { - setSelectedWallAsset(null); - } - }} - onPointerEnter={(e) => { - if (!toggleView) { - e.stopPropagation(); - let currentObject = e.object as THREE.Object3D; - while (currentObject) { - if (currentObject.userData.wallUuid) { - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - handlePointerOver(wallAsset, currentObject); - } - }} - onPointerLeave={(e) => { - if (!toggleView) { - e.stopPropagation(); - handlePointerOut(e, wallAsset); - } - }} + onPointerDown={handlePointerDown} + onClick={handleClick} + onPointerMissed={handlePointerMissed} + onPointerEnter={handlePointerEnter} + onPointerLeave={handlePointerLeave} userData={wallAsset} >