import { useEffect } from "react"; import * as THREE from 'three'; import gsap from 'gsap'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { toast } from 'react-toastify'; import { useSocketStore, useActiveLayer, useWallItems, useFloorItems, useLayers, useUpdateScene, useWalls, useDeletedLines, useNewLines, useZonePoints, useZones } from "../../../store/store"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from '../../../types/world/worldConstants'; import TempLoader from "../../builder/geomentries/assets/tempLoader"; // 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"; export default function SocketResponses({ floorPlanGroup, lines, floorGroup, floorGroupAisle, scene, onlyFloorlines, AssetConfigurations, itemsGroup, isTempLoader, tempLoader, currentLayerPoint, floorPlanGroupPoint, floorPlanGroupLine, zoneGroup, dragPointControls }: any) { const { socket } = useSocketStore(); const { activeLayer, setActiveLayer } = useActiveLayer(); const { wallItems, setWallItems } = useWallItems(); const { layers, setLayers } = useLayers(); const { floorItems, setFloorItems } = useFloorItems(); const { updateScene, setUpdateScene } = useUpdateScene(); const { walls, setWalls } = useWalls(); const { deletedLines, setDeletedLines } = useDeletedLines(); const { newLines, setNewLines } = useNewLines(); const { zones, setZones } = useZones(); const { zonePoints, setZonePoints } = useZonePoints(); useEffect(() => { const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; if (!socket) return socket.on('cameraCreateResponse', (data: any) => { // console.log('data: ', data); }) socket.on('userConnectRespones', (data: any) => { // console.log('data: ', data); }) socket.on('userDisConnectRespones', (data: any) => { // console.log('data: ', data); }) socket.on('cameraUpdateResponse', (data: any) => { // console.log('data: ', data); }) socket.on('EnvironmentUpdateResponse', (data: any) => { // console.log('data: ', data); }) socket.on('model-asset:response:updates', async (data: any) => { // console.log('data: ', data); if (socket.id === data.socketId) { return } if (organization !== data.organization) { return } if (data.message === "Model created successfully") { 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); let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; try { isTempLoader.current = true; const cachedModel = THREE.Cache.get(data.data.modelName); let url; if (cachedModel) { // console.log(`Getting ${data.data.modelName} from cache`); const model = cachedModel.scene.clone(); model.uuid = data.data.modelUuid; model.userData = { name: data.data.modelName, modelId: data.data.modelfileID, modelUuid: data.data.modelUuid }; model.position.set(...data.data.position as [number, number, number]); model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.traverse((child: any) => { if (child.isMesh) { // Clone the material to ensure changes are independent // child.material = child.material.clone(); child.castShadow = true; child.receiveShadow = true; } }); itemsGroup.current.add(model); if (tempLoader.current) { tempLoader.current.material.dispose(); tempLoader.current.geometry.dispose(); itemsGroup.current.remove(tempLoader.current); tempLoader.current = undefined; } const newFloorItem: Types.FloorItemType = { modelUuid: data.data.modelUuid, modelName: data.data.modelName, modelfileID: data.data.modelfileID, position: [...data.data.position as [number, number, number]], rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z, }, isLocked: data.data.isLocked, isVisible: data.data.isVisible, }; setFloorItems((prevItems: any) => { const updatedItems = [...(prevItems || []), newFloorItem]; localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); return updatedItems; }); gsap.to(model.position, { y: data.data.position[1], duration: 1.5, ease: 'power2.out' }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out', onComplete: () => { toast.success("Model Added!") } }); } else { const indexedDBModel = await retrieveGLTF(data.data.modelName); if (indexedDBModel) { // console.log(`Getting ${data.data.modelName} from IndexedDB`); url = URL.createObjectURL(indexedDBModel); } else { // console.log(`Getting ${data.data.modelName} from Backend`); url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; const modelBlob = await fetch(url).then((res) => res.blob()); await storeGLTF(data.data.modelfileID, modelBlob); } } if (url) { loadModel(url); } } catch (error) { console.error('Error fetching asset model:', error); } function loadModel(url: string) { loader.load(url, (gltf) => { URL.revokeObjectURL(url); THREE.Cache.remove(url); const model = gltf.scene; model.uuid = data.data.modelUuid; model.userData = { name: data.data.modelName, modelId: data.data.modelfileID, modelUuid: data.data.modelUuid }; model.position.set(...data.data.position as [number, number, number]); model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.traverse((child: any) => { if (child.isMesh) { // Clone the material to ensure changes are independent // child.material = child.material.clone(); child.castShadow = true; child.receiveShadow = true; } }); itemsGroup.current.add(model); if (tempLoader.current) { tempLoader.current.material.dispose(); tempLoader.current.geometry.dispose(); itemsGroup.current.remove(tempLoader.current); tempLoader.current = undefined; } const newFloorItem: Types.FloorItemType = { modelUuid: data.data.modelUuid, modelName: data.data.modelName, modelfileID: data.data.modelfileID, position: [...data.data.position as [number, number, number]], rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z, }, isLocked: data.data.isLocked, isVisible: data.data.isVisible, }; setFloorItems((prevItems: any) => { const updatedItems = [...(prevItems || []), newFloorItem]; localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); return updatedItems; }); gsap.to(model.position, { y: data.data.position[1], duration: 1.5, ease: 'power2.out' }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out', onComplete: () => { toast.success("Model Added!") } }); THREE.Cache.add(data.data.modelName, gltf); }, () => { TempLoader(new THREE.Vector3(...data.data.position), isTempLoader, tempLoader, itemsGroup); }); } } else if (data.message === "Model updated successfully") { itemsGroup.current?.children.forEach((item: THREE.Group) => { if (item.uuid === data.data.modelUuid) { item.position.set(...data.data.position as [number, number, number]); item.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); } }) setFloorItems((prevItems: Types.FloorItems) => { if (!prevItems) { return } let updatedItem: any = null; const updatedItems = prevItems.map((item) => { if (item.modelUuid === data.data.modelUuid) { updatedItem = { ...item, position: [...data.data.position] as [number, number, number], rotation: { x: data.data.rotation.x, y: data.data.rotation.y, z: data.data.rotation.z, }, }; return updatedItem; } return item; }); return updatedItems; }) } }) socket.on('model-asset:response:updates', (data: any) => { if (socket.id === data.socketId) { return } if (organization !== data.organization) { return } if (data.message === "Model deleted successfully") { const deletedUUID = data.data.modelUuid; let items = JSON.parse(localStorage.getItem("FloorItems")!); const updatedItems = items.filter( (item: { modelUuid: string }) => item.modelUuid !== deletedUUID ); const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== deletedUUID); localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); itemsGroup.current.children.forEach((item: any) => { if (item.uuid === deletedUUID) { itemsGroup.current.remove(item); } }) setFloorItems(updatedItems); toast.success("Model Removed!"); } }) socket.on('Line:response:update', (data: any) => { 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('Line:response:delete', (data: any) => { 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); toast.success("Line Removed!"); } }) socket.on('Line:response:delete:point', (data: any) => { 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); toast.success("Point Removed!"); } }) socket.on('Line:response:delete:layer', async (data: any) => { 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); const highestLayer = Math.max( 1, lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), zonesData.data.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) ); setLayers(highestLayer); loadWalls(lines, setWalls); setUpdateScene(true); toast.success("Layer Removed!"); } }) }, [socket]) useEffect(() => { if (!socket) return const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; socket.on('wallItemsDeleteResponse', (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)); toast.success("Model Removed!"); }, 50); } }) socket.on('wallItemsUpdateResponse', (data: any) => { if (socket.id === data.socketId) { return } if (organization !== data.organization) { return } if (data.message === "wallIitem created") { const loader = new GLTFLoader(); loader.load(AssetConfigurations[data.data.modelName].modelUrl, async (gltf) => { const model = gltf.scene; 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, scale: data.data.scale, csgscale: data.data.csgscale, csgposition: data.data.csgposition, position: data.data.position, quaternion: data.data.quaternion }; setWallItems((prevItems: any) => { const updatedItems = [...prevItems, newWallItem]; const WallItemsForStorage = updatedItems.map(item => { const { model, ...rest } = item; return { ...rest, modelUuid: model?.uuid, }; }); localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); toast.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)); toast.success("Model Updated!"); return updatedItems; }); } }) return () => { socket.off('wallItemsDeleteResponse'); socket.off('wallItemsUpdateResponse'); }; }, [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('Line:response:create', async (data: any) => { 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: { uColor: { 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 uColor; 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(uColor, 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); const highestLayer = Math.max( 1, lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), zonesData.data.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('Line:response:create'); }; }, [socket, activeLayer]) useEffect(() => { if (!socket) return const email = localStorage.getItem('email'); const organization = (email!.split("@")[1]).split(".")[0]; socket.on('zone:response:updates', (data: any) => { 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.zoneId === data.data.zoneId ? data.data : zone ); setZones(updatedZones); setUpdateScene(true); } }) socket.on('zone:response:delete', (data: any) => { if (socket.id === data.socketId) { return } if (organization !== data.organization) { return } if (data.message === "zone deleted") { const updatedZones = zones.filter((zone: any) => zone.zoneId !== data.data.zoneId); setZones(updatedZones); const zoneIndex = zones.findIndex((zone: any) => zone.zoneId === data.data.zoneId); 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('zone:response:updates'); socket.off('zone:response:delete'); }; }, [socket, zones, zonePoints]) return ( <> ) }