import { useEffect } from "react"; import * as THREE from "three"; import gsap from "gsap"; import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { useSocketStore, useActiveLayer, useWallItems, useLayers, useUpdateScene, useWalls, useDeletedLines, useNewLines, useZonePoints, useZones, } from "../../../store/builder/store"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; // import { setFloorItemApi } from "../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import objectLineToArray from "../../builder/geomentries/lines/lineConvertions/objectLineToArray"; import addLineToScene from "../../builder/geomentries/lines/addLineToScene"; import updateLinesPositions from "../../builder/geomentries/lines/updateLinesPositions"; import updateLines from "../../builder/geomentries/lines/updateLines"; import updateDistanceText from "../../builder/geomentries/lines/updateDistanceText"; import updateFloorLines from "../../builder/geomentries/floors/updateFloorLines"; import loadWalls from "../../builder/geomentries/walls/loadWalls"; import RemoveConnectedLines from "../../builder/geomentries/lines/removeConnectedLines"; import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; import { useParams } from "react-router-dom"; import { useAssetsStore } from "../../../store/builder/useAssetStore"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../store/simulation/useProductStore"; export default function SocketResponses({ floorPlanGroup, lines, floorGroup, floorGroupAisle, scene, onlyFloorlines, itemsGroup, isTempLoader, tempLoader, currentLayerPoint, floorPlanGroupPoint, floorPlanGroupLine, zoneGroup, dragPointControls, }: any) { const { socket } = useSocketStore(); const { activeLayer, setActiveLayer } = useActiveLayer(); const { wallItems, setWallItems } = useWallItems(); const { setLayers } = useLayers(); const { setUpdateScene } = useUpdateScene(); const { setWalls } = useWalls(); const { setDeletedLines } = useDeletedLines(); const { setNewLines } = useNewLines(); const { zones, setZones } = useZones(); const { zonePoints, setZonePoints } = useZonePoints(); const { projectId } = useParams(); const { addAsset, updateAsset, removeAsset } = useAssetsStore(); useEffect(() => { const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; if (!socket) return; socket.on("cameraCreateResponse", (data: any) => { // }); socket.on("userConnectRespones", (data: any) => { // }); socket.on("userDisConnectRespones", (data: any) => { // }); socket.on("v1:camera:Response:update", (data: any) => { console.log('dataCamera: ', data); // }); socket.on("EnvironmentUpdateResponse", (data: any) => { // }); socket.on("v1:model-asset:response:add", async (data: any) => { // if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } console.log('data.data: ', data); if (data.message === "Model created successfully") { try { const asset: Asset = { modelUuid: data.data.modelUuid, modelName: data.data.modelName, assetId: data.data.modelfileID, position: data.data.position, rotation: [data.data.rotation.x, data.data.rotation.y, data.data.rotation.z], isLocked: data.data.isLocked, isCollidable: false, isVisible: data.data.isVisible, opacity: 1, } addAsset(asset); echo.success("Added model through collaboration"); } catch (error) { echo.error("Failed to create model through collaboration"); } } else if (data.message === "Model updated successfully") { try { const asset: Asset = { modelUuid: data.data.modelUuid, modelName: data.data.modelName, assetId: data.data.modelfileID, position: data.data.position, rotation: [data.data.rotation.x, data.data.rotation.y, data.data.rotation.z], isLocked: data.data.isLocked, isCollidable: false, isVisible: data.data.isVisible, opacity: 1, } updateAsset(asset.modelUuid, { position: asset.position, rotation: asset.rotation, }); echo.success("Updated model through collaboration"); } catch (error) { echo.error("Failed to update model through collaboration"); } } else { echo.error("Failed executing action from collaboration"); } }); socket.on("v1:model-asset:response:delete", (data: any) => { if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "Model deleted successfully") { try { const deletedUUID = data.data.modelUuid; useEventsStore.getState().removeEvent(deletedUUID); useProductStore.getState().deleteEvent(deletedUUID); removeAsset(deletedUUID); echo.success("Model Removed successfully through collaboration"); } catch (error) { echo.error("Failed to remove model through collaboration"); } } }); socket.on("v1:Line:response:update", (data: any) => { console.log('data: ', data); if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "line updated") { const DraggedUUID = data.data.uuid; const DraggedPosition = new THREE.Vector3( data.data.position.x, data.data.position.y, data.data.position.z ); const point = floorPlanGroupPoint.current.getObjectByProperty( "uuid", DraggedUUID ); point.position.set( DraggedPosition.x, DraggedPosition.y, DraggedPosition.z ); const affectedLines = updateLinesPositions( { uuid: DraggedUUID, position: DraggedPosition }, lines ); updateLines(floorPlanGroupLine, affectedLines); updateDistanceText(scene, floorPlanGroupLine, affectedLines); updateFloorLines(onlyFloorlines, { uuid: DraggedUUID, position: DraggedPosition, }); loadWalls(lines, setWalls); setUpdateScene(true); } }); socket.on("v1:Line:response:delete", (data: any) => { console.log('data: ', data); if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "line deleted") { const line = objectLineToArray(data.data); const linePoints = line; const connectedpoints = [linePoints[0][1], linePoints[1][1]]; onlyFloorlines.current = onlyFloorlines.current .map((floorline: any) => floorline.filter( (line: any) => line[0][1] !== connectedpoints[0] && line[1][1] !== connectedpoints[1] ) ) .filter((floorline: any) => floorline.length > 0); const removedLine = lines.current.find( (item: any) => (item[0][1] === linePoints[0][1] && item[1][1] === linePoints[1][1]) || (item[0][1] === linePoints[1][1] && item[1][1] === linePoints[0][1]) ); lines.current = lines.current.filter( (item: any) => item !== removedLine ); floorPlanGroupLine.current.children.forEach((line: any) => { const linePoints = line.userData.linePoints as [ number, string, number ][]; const uuid1 = linePoints[0][1]; const uuid2 = linePoints[1][1]; if ( (uuid1 === connectedpoints[0] && uuid2 === connectedpoints[1]) || (uuid1 === connectedpoints[1] && uuid2 === connectedpoints[0]) ) { line.material.dispose(); line.geometry.dispose(); floorPlanGroupLine.current.remove(line); setDeletedLines([line.userData.linePoints]); } }); connectedpoints.forEach((pointUUID) => { let isConnected = false; floorPlanGroupLine.current.children.forEach((line: any) => { const linePoints = line.userData.linePoints; const uuid1 = linePoints[0][1]; const uuid2 = linePoints[1][1]; if (uuid1 === pointUUID || uuid2 === pointUUID) { isConnected = true; } }); if (!isConnected) { floorPlanGroupPoint.current.children.forEach((point: any) => { if (point.uuid === pointUUID) { point.material.dispose(); point.geometry.dispose(); floorPlanGroupPoint.current.remove(point); } }); } }); loadWalls(lines, setWalls); setUpdateScene(true); echo.success("Line Removed!"); } }); socket.on("v1:Line:response:delete:point", (data: any) => { console.log('datapoint: ', data); if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "point deleted") { const point = floorPlanGroupPoint.current?.getObjectByProperty( "uuid", data.data ); point.material.dispose(); point.geometry.dispose(); floorPlanGroupPoint.current.remove(point); onlyFloorlines.current = onlyFloorlines.current .map((floorline: any) => floorline.filter( (line: any) => line[0][1] !== data.data && line[1][1] !== data.data ) ) .filter((floorline: any) => floorline.length > 0); RemoveConnectedLines( data.data, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines ); loadWalls(lines, setWalls); setUpdateScene(true); echo.success("Point Removed!"); } }); socket.on("v1:Line:response:delete:layer", async (data: any) => { console.log('data: ', data); if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "layer deleted") { setActiveLayer(1); const removedLayer = data.data; const removedLines: Types.Lines = lines.current.filter( (line: any) => line[0][2] === removedLayer ); ////////// Remove Points and lines from the removed layer ////////// removedLines.forEach(async (line) => { line.forEach(async (removedPoint) => { const removableLines: THREE.Mesh[] = []; const connectedpoints: string[] = []; floorPlanGroupLine.current.children.forEach((line: any) => { const linePoints = line.userData.linePoints as [ number, string, number ][]; const uuid1 = linePoints[0][1]; const uuid2 = linePoints[1][1]; if (uuid1 === removedPoint[1] || uuid2 === removedPoint[1]) { connectedpoints.push(uuid1 === removedPoint[1] ? uuid2 : uuid1); removableLines.push(line as THREE.Mesh); } }); if (removableLines.length > 0) { removableLines.forEach((line: any) => { lines.current = lines.current.filter( (item: any) => JSON.stringify(item) !== JSON.stringify(line.userData.linePoints) ); line.material.dispose(); line.geometry.dispose(); floorPlanGroupLine.current.remove(line); }); } const point = floorPlanGroupPoint.current.getObjectByProperty( "uuid", removedPoint[1] ); if (point) { point.material.dispose(); point.geometry.dispose(); floorPlanGroupPoint.current.remove(point); } }); }); ////////// Update the remaining lines layer values in the userData and in lines.current ////////// let remaining = lines.current.filter( (line: any) => line[0][2] !== removedLayer ); let updatedLines: Types.Lines = []; remaining.forEach((line: any) => { let newLines = JSON.parse(JSON.stringify(line)); if (newLines[0][2] > removedLayer) { newLines[0][2] -= 1; newLines[1][2] -= 1; } const matchingLine = floorPlanGroupLine.current.children.find( (l: any) => l.userData.linePoints[0][1] === line[0][1] && l.userData.linePoints[1][1] === line[1][1] ); if (matchingLine) { const updatedUserData = JSON.parse( JSON.stringify(matchingLine.userData) ); updatedUserData.linePoints[0][2] = newLines[0][2]; updatedUserData.linePoints[1][2] = newLines[1][2]; matchingLine.userData = updatedUserData; } updatedLines.push(newLines); }); lines.current = updatedLines; localStorage.setItem("Lines", JSON.stringify(lines.current)); ////////// Also remove OnlyFloorLines and update it in localstorage ////////// onlyFloorlines.current = onlyFloorlines.current.filter((floor: any) => { return floor[0][0][2] !== removedLayer; }); const meshToRemove = floorGroup.current?.children.find( (mesh: any) => mesh.name === `Only_Floor_Line_${removedLayer}` ); if (meshToRemove) { meshToRemove.geometry.dispose(); meshToRemove.material.dispose(); floorGroup.current?.remove(meshToRemove); } const zonesData = await getZonesApi(organization,projectId); const highestLayer = Math.max( 1, lines.current.reduce( (maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0 ), zonesData.reduce( (maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0 ) ); setLayers(highestLayer); loadWalls(lines, setWalls); setUpdateScene(true); echo.success("Layer Removed!"); } }); }, [socket]); useEffect(() => { if (!socket) return; const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; socket.on("v1:wallItem:Response:Delete", (data: any) => { if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "wallitem deleted") { const deletedUUID = data.data.modelUuid; let WallItemsRef = wallItems; const Items = WallItemsRef.filter((item: any) => item.model?.uuid !== deletedUUID); setWallItems([]); setTimeout(async () => { WallItemsRef = Items; setWallItems(WallItemsRef); const WallItemsForStorage = WallItemsRef.map((item: any) => { const { model, ...rest } = item; return { ...rest, modelUuid: model?.uuid, }; }); localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); echo.success("Model Removed!"); }, 50); } }); socket.on("v1:wallItems:Response:Update", (data: any) => { // if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "wallIitem created") { const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); loader.setDRACOLoader(dracoLoader); // Check THREE.js cache first const cachedModel = THREE.Cache.get(data.data.modelfileID); if (cachedModel) { handleModelLoad(cachedModel); return; } // Check IndexedDB cache retrieveGLTF(data.data.modelfileID).then((cachedModelBlob) => { if (cachedModelBlob) { const blobUrl = URL.createObjectURL(cachedModelBlob); loader.load(blobUrl, (gltf) => { URL.revokeObjectURL(blobUrl); THREE.Cache.remove(blobUrl); THREE.Cache.add(data.data.modelfileID, gltf); handleModelLoad(gltf); }); return; } }) // Load from backend if not in any cache loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`, async (gltf) => { try { const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`).then((res) => res.blob()); await storeGLTF(data.data.modelfileID, modelBlob); THREE.Cache.add(data.data.modelfileID, gltf); await handleModelLoad(gltf); } catch (error) { handleModelLoad(gltf); } }); async function handleModelLoad(gltf: GLTF) { const model = gltf.scene.clone(); model.uuid = data.data.modelUuid; model.children[0].children.forEach((child) => { if (child.name !== "CSG_REF") { child.castShadow = true; child.receiveShadow = true; } }); const newWallItem = { type: data.data.type, model: model, modelName: data.data.modelName, modelfileID: data.data.modelfileID, scale: data.data.scale, csgscale: data.data.csgscale, csgposition: data.data.csgposition, position: data.data.position, quaternion: data.data.quaternion, }; setWallItems((prevItems: Types.wallItems) => { const updatedItems = [...prevItems, newWallItem]; const WallItemsForStorage = updatedItems.map(item => { const { model, ...rest } = item; return { ...rest, modelUuid: model?.uuid, }; }); localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); echo.success("Model Added!"); return updatedItems; }); } } else if (data.message === "wallIitem updated") { const updatedUUID = data.data.modelUuid; setWallItems((prevItems: any) => { const updatedItems = prevItems.map((item: any) => { if (item.model.uuid === updatedUUID) { return { ...item, position: data.data.position, quaternion: data.data.quaternion, scale: data.data.scale, csgscale: data.data.csgscale, csgposition: data.data.csgposition, }; } return item; }); const WallItemsForStorage = updatedItems.map((item: any) => { const { model, ...rest } = item; return { ...rest, modelUuid: model?.uuid, }; }); localStorage.setItem( "WallItems", JSON.stringify(WallItemsForStorage) ); echo.success("Model Updated!"); return updatedItems; }); } }); return () => { socket.off("v1:wallItem:Response:Delete"); socket.off("v1:wallItems:Response:Update"); }; }, [wallItems]); function getPointColor(lineType: string | undefined): string { switch (lineType) { case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor; case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor; case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor; default: return CONSTANTS.pointConfig.defaultOuterColor; } } function getLineColor(lineType: string | undefined): string { switch (lineType) { case CONSTANTS.lineConfig.wallName: return CONSTANTS.lineConfig.wallColor; case CONSTANTS.lineConfig.floorName: return CONSTANTS.lineConfig.floorColor; case CONSTANTS.lineConfig.aisleName: return CONSTANTS.lineConfig.aisleColor; default: return CONSTANTS.lineConfig.defaultColor; } } useEffect(() => { if (!socket) return; const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; socket.on("v1:Line:response:create", async (data: any) => { console.log('data: ', data); // if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "line create") { const line: Types.Line = objectLineToArray(data.data); const type = line[0][3]; const pointColour = getPointColor(type); const lineColour = getLineColor(type); setNewLines([line]); line.forEach((line) => { const existingPoint = floorPlanGroupPoint.current?.getObjectByProperty("uuid", line[1]); if (existingPoint) { return; } const geometry = new THREE.BoxGeometry( ...CONSTANTS.pointConfig.boxScale ); const material = new THREE.ShaderMaterial({ uniforms: { uOuterColor: { value: new THREE.Color(pointColour) }, // Blue color for the border uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor), }, // White color for the inner square }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` varying vec2 vUv; uniform vec3 uOuterColor; uniform vec3 uInnerColor; void main() { // Define the size of the white square as a proportion of the face float borderThickness = 0.2; // Adjust this value for border thickness if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { gl_FragColor = vec4(uInnerColor, 1.0); // White inner square } else { gl_FragColor = vec4(uOuterColor, 1.0); // Blue border } } `, }); const point = new THREE.Mesh(geometry, material); point.name = "point"; point.uuid = line[1]; point.userData = { type: type, color: pointColour }; point.position.set(line[0].x, line[0].y, line[0].z); currentLayerPoint.current.push(point); floorPlanGroupPoint.current?.add(point); }); if (dragPointControls.current) { dragPointControls.current!.objects = currentLayerPoint.current; } addLineToScene( new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), lineColour, line, floorPlanGroupLine ); lines.current.push(line); const zonesData = await getZonesApi(organization,projectId); const highestLayer = Math.max( 1, lines.current.reduce( (maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0 ), zonesData.reduce( (maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0 ) ); setLayers(highestLayer); Layer2DVisibility( activeLayer, floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, currentLayerPoint, dragPointControls ); loadWalls(lines, setWalls); setUpdateScene(true); } }); return () => { socket.off("v1:Line:response:create"); }; }, [socket, activeLayer]); useEffect(() => { if (!socket) return; const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; socket.on("v1:zone:response:updates", (data: any) => { console.log('data: ', data); if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "zone created") { const pointsArray: [number, number, number][] = data.data.points; const vector3Array = pointsArray.map( ([x, y, z]) => new THREE.Vector3(x, y, z) ); const newZones = [...zones, data.data]; setZones(newZones); const updatedZonePoints = [...zonePoints, ...vector3Array]; setZonePoints(updatedZonePoints); const highestLayer = Math.max( 1, lines.current.reduce( (maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0 ), newZones.reduce( (maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0 ) ); setLayers(highestLayer); setUpdateScene(true); } if (data.message === "zone updated") { const updatedZones = zones.map((zone: any) => zone.zoneUuid === data.data.zoneUuid ? data.data : zone ); setZones(updatedZones); setUpdateScene(true); } }); socket.on("v1:zone:response:delete", (data: any) => { console.log('data: ', data); if (socket.id === data.socketId) { return; } if (organization !== data.organization) { return; } if (data.message === "zone deleted") { const updatedZones = zones.filter( (zone: any) => zone.zoneUuid !== data.data.zoneUuid ); setZones(updatedZones); const zoneIndex = zones.findIndex( (zone: any) => zone.zoneUuid === data.data.zoneUuid ); if (zoneIndex !== -1) { const updatedzonePoints = zonePoints.filter( (_: any, index: any) => index < zoneIndex * 4 || index >= zoneIndex * 4 + 4 ); setZonePoints(updatedzonePoints); } const highestLayer = Math.max( 1, lines.current.reduce( (maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0 ), updatedZones.reduce( (maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0 ) ); setLayers(highestLayer); setUpdateScene(true); } }); return () => { socket.off("v1:zone:response:updates"); socket.off("v1:zone:response:delete"); }; }, [socket, zones, zonePoints]); return <>; }