feat: Implement wall asset management APIs and socket integration for create, update, and delete operations
This commit is contained in:
@@ -1,18 +1,25 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { useThree } from '@react-three/fiber';
|
||||||
|
import { Base, Geometry, Subtraction } from '@react-three/csg';
|
||||||
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
|
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
|
||||||
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||||
import { Base, Geometry, Subtraction } from '@react-three/csg';
|
|
||||||
import useModuleStore from '../../../../../store/useModuleStore';
|
import useModuleStore from '../../../../../store/useModuleStore';
|
||||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||||
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
||||||
import { useActiveTool, useToggleView } from '../../../../../store/builder/store';
|
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 closestPointOnLineSegment from '../../../line/helpers/getClosestPointOnLineSegment';
|
||||||
import { useThree } from '@react-three/fiber';
|
|
||||||
|
import { upsertWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
||||||
|
import { deleteWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
|
||||||
|
|
||||||
function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||||
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||||
|
const { socket } = useSocketStore();
|
||||||
const { raycaster, pointer, camera, scene, controls, gl } = useThree();
|
const { raycaster, pointer, camera, scene, controls, gl } = useThree();
|
||||||
const { wallStore, wallAssetStore } = useSceneContext();
|
const { wallStore, wallAssetStore } = useSceneContext();
|
||||||
const { walls, getWallById } = wallStore();
|
const { walls, getWallById } = wallStore();
|
||||||
@@ -26,6 +33,10 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|||||||
const groupRef = useRef<THREE.Group>(null);
|
const groupRef = useRef<THREE.Group>(null);
|
||||||
const wall = useMemo(() => getWallById(wallAsset.wallUuid), [getWallById, wallAsset.wallUuid, walls]);
|
const wall = useMemo(() => getWallById(wallAsset.wallUuid), [getWallById, wallAsset.wallUuid, walls]);
|
||||||
const draggingRef = useRef(false);
|
const draggingRef = useRef(false);
|
||||||
|
const { selectedVersionStore } = useVersionContext();
|
||||||
|
const { selectedVersion } = selectedVersionStore();
|
||||||
|
const { userId, organization } = getUserData();
|
||||||
|
const { projectId } = useParams();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loader = new GLTFLoader();
|
const loader = new GLTFLoader();
|
||||||
@@ -131,11 +142,31 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|||||||
|
|
||||||
const wallRotation = intersect.object.rotation.clone();
|
const wallRotation = intersect.object.rotation.clone();
|
||||||
|
|
||||||
updateWallAsset(wallAsset.modelUuid, {
|
const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, {
|
||||||
wallUuid: intersect.object.userData.wallUuid,
|
wallUuid: intersect.object.userData.wallUuid,
|
||||||
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z],
|
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z],
|
||||||
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (projectId && updatedWallAsset) {
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
|
||||||
|
|
||||||
|
// SOCKET
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
wallAssetData: updatedWallAsset,
|
||||||
|
projectId: projectId,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
userId: userId,
|
||||||
|
organization: organization
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('v1:wall-asset:add', data);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,13 +180,34 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|||||||
canvasElement.removeEventListener('pointerup', onPointerUp);
|
canvasElement.removeEventListener('pointerup', onPointerUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
}, [gl, camera, toggleView, activeModule, selectedWallAsset])
|
}, [gl, camera, toggleView, activeModule, selectedWallAsset, socket])
|
||||||
|
|
||||||
const handlePointerClick = useCallback((wallAsset: WallAsset) => {
|
const handlePointerClick = useCallback((wallAsset: WallAsset) => {
|
||||||
if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
||||||
removeWallAsset(wallAsset.modelUuid);
|
const removedWallAsset = removeWallAsset(wallAsset.modelUuid);
|
||||||
|
|
||||||
|
if (projectId && removedWallAsset) {
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
// deleteWallAssetApi(projectId, selectedVersion?.versionId || '', removedWallAsset.modelUuid, removedWallAsset.wallUuid);
|
||||||
|
|
||||||
|
// SOCKET
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
projectId: projectId,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
userId: userId,
|
||||||
|
organization: organization,
|
||||||
|
modelUuid: removedWallAsset.modelUuid,
|
||||||
|
wallUuid: removedWallAsset.wallUuid
|
||||||
}
|
}
|
||||||
}, [activeTool, activeModule, deletableWallAsset])
|
|
||||||
|
socket.emit('v1:wall-asset:delete', data);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeTool, activeModule, deletableWallAsset, socket])
|
||||||
|
|
||||||
const handlePointerOver = useCallback((wallAsset: WallAsset, currentObject: THREE.Object3D) => {
|
const handlePointerOver = useCallback((wallAsset: WallAsset, currentObject: THREE.Object3D) => {
|
||||||
if (activeTool === "delete" && activeModule === 'builder') {
|
if (activeTool === "delete" && activeModule === 'builder') {
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { useThree } from '@react-three/fiber';
|
import { useThree } from '@react-three/fiber';
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useSelectedItem, useSocketStore, useToggleView } from '../../../store/builder/store';
|
|
||||||
import useModuleStore from '../../../store/useModuleStore';
|
import useModuleStore from '../../../store/useModuleStore';
|
||||||
|
import { useSelectedItem, useSocketStore, useToggleView } from '../../../store/builder/store';
|
||||||
import { MathUtils, Vector3 } from 'three';
|
import { MathUtils, Vector3 } from 'three';
|
||||||
import { useSceneContext } from '../../scene/sceneContext';
|
import { useSceneContext } from '../../scene/sceneContext';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { useVersionContext } from '../version/versionContext';
|
||||||
|
import { getUserData } from '../../../functions/getUserData';
|
||||||
import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegment';
|
import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegment';
|
||||||
|
|
||||||
|
import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
||||||
|
|
||||||
function WallAssetCreator() {
|
function WallAssetCreator() {
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { pointer, camera, raycaster, scene, gl } = useThree();
|
const { pointer, camera, raycaster, scene, gl } = useThree();
|
||||||
@@ -14,6 +19,10 @@ function WallAssetCreator() {
|
|||||||
const { wallAssetStore } = useSceneContext();
|
const { wallAssetStore } = useSceneContext();
|
||||||
const { addWallAsset } = wallAssetStore();
|
const { addWallAsset } = wallAssetStore();
|
||||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||||
|
const { selectedVersionStore } = useVersionContext();
|
||||||
|
const { selectedVersion } = selectedVersionStore();
|
||||||
|
const { userId, organization } = getUserData();
|
||||||
|
const { projectId } = useParams();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvasElement = gl.domElement;
|
const canvasElement = gl.domElement;
|
||||||
@@ -52,6 +61,25 @@ function WallAssetCreator() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
addWallAsset(newWallAsset);
|
addWallAsset(newWallAsset);
|
||||||
|
if (projectId) {
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', newWallAsset);
|
||||||
|
|
||||||
|
// SOCKET
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
wallAssetData: newWallAsset,
|
||||||
|
projectId: projectId,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
userId: userId,
|
||||||
|
organization: organization
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('v1:wall-asset:add', data);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
import useModuleStore from '../../../store/useModuleStore';
|
import useModuleStore from '../../../store/useModuleStore';
|
||||||
import WallAssetCreator from './wallAssetCreator'
|
import WallAssetCreator from './wallAssetCreator'
|
||||||
import WallAssetInstances from './Instances/wallAssetInstances'
|
import WallAssetInstances from './Instances/wallAssetInstances'
|
||||||
|
import { getWallAssetsApi } from '../../../services/factoryBuilder/asset/wallAsset/getWallAssetsApi';
|
||||||
|
|
||||||
function WallAssetGroup() {
|
function WallAssetGroup() {
|
||||||
const { togglView } = useToggleView();
|
const { togglView } = useToggleView();
|
||||||
@@ -26,6 +27,21 @@ function WallAssetGroup() {
|
|||||||
setDeletableWallAsset(null);
|
setDeletableWallAsset(null);
|
||||||
}, [togglView, activeModule, activeTool])
|
}, [togglView, activeModule, activeTool])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (projectId && selectedVersion) {
|
||||||
|
getWallAssetsApi(projectId, selectedVersion?.versionId || '').then((wallAssets) => {
|
||||||
|
console.log('wallAssets: ', wallAssets);
|
||||||
|
if (wallAssets && wallAssets.length > 0) {
|
||||||
|
setWallAssets(wallAssets);
|
||||||
|
} else {
|
||||||
|
setWallAssets([]);
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [projectId, selectedVersion?.versionId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
import React from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { useSocketStore } from '../../../store/builder/store';
|
||||||
|
|
||||||
export default function SocketResponses() {
|
export default function SocketResponses() {
|
||||||
|
const { socket } = useSocketStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.on("v1:wall-asset:response:delete", (data: any) => {
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("v1:wall-asset:response:delete");
|
||||||
|
}
|
||||||
|
}, [socket])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
|
||||||
|
export const deleteWallAssetApi = async (
|
||||||
|
projectId: string,
|
||||||
|
versionId: string,
|
||||||
|
modelUuid: string,
|
||||||
|
wallUuid: string,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${url_Backend_dwinzo}/api/V1/delete/wallasset`, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ projectId, versionId, modelUuid, wallUuid }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to delete wall asset:", response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
echo.error("Failed to delete wall asset");
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.log(error.message);
|
||||||
|
} else {
|
||||||
|
console.log("An unknown error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
|
||||||
|
export const getWallAssetsApi = async (
|
||||||
|
projectId: string,
|
||||||
|
versionId: string,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${url_Backend_dwinzo}/api/V1/wallassets/${projectId}/${versionId}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to get wall assets:", response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
echo.error("Failed to get wall assets");
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.log(error.message);
|
||||||
|
} else {
|
||||||
|
console.log("An unknown error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
|
||||||
|
export const upsertWallAssetApi = async (
|
||||||
|
projectId: string,
|
||||||
|
versionId: string,
|
||||||
|
wallAssetData: WallAsset
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${url_Backend_dwinzo}/api/V1/wall-asset`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ projectId, versionId, wallAssetData }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to upsert wall asset:", response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
echo.error("Failed to upsert wall asset");
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.log(error.message);
|
||||||
|
} else {
|
||||||
|
console.log("An unknown error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,9 +5,9 @@ interface WallAssetStore {
|
|||||||
wallAssets: WallAsset[];
|
wallAssets: WallAsset[];
|
||||||
setWallAssets: (assets: WallAsset[]) => void;
|
setWallAssets: (assets: WallAsset[]) => void;
|
||||||
addWallAsset: (asset: WallAsset) => void;
|
addWallAsset: (asset: WallAsset) => void;
|
||||||
updateWallAsset: (uuid: string, updated: Partial<WallAsset>) => void;
|
updateWallAsset: (uuid: string, updated: Partial<WallAsset>) => WallAsset | undefined;
|
||||||
setWallAssetPosition: (uuid: string, position: [number, number, number]) => void;
|
setWallAssetPosition: (uuid: string, position: [number, number, number]) => void;
|
||||||
removeWallAsset: (uuid: string) => void;
|
removeWallAsset: (uuid: string) => WallAsset | undefined;
|
||||||
clearWallAssets: () => void;
|
clearWallAssets: () => void;
|
||||||
|
|
||||||
setVisibility: (uuid: string, isVisible: boolean) => void;
|
setVisibility: (uuid: string, isVisible: boolean) => void;
|
||||||
@@ -31,12 +31,17 @@ export const createWallAssetStore = () => {
|
|||||||
state.wallAssets.push(asset);
|
state.wallAssets.push(asset);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateWallAsset: (uuid, updated) => set(state => {
|
updateWallAsset: (uuid, updated) => {
|
||||||
|
let updatedWallAsset: WallAsset | undefined;
|
||||||
|
set(state => {
|
||||||
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
Object.assign(asset, updated);
|
Object.assign(asset, updated);
|
||||||
|
updatedWallAsset = JSON.parse(JSON.stringify(asset));
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
|
return updatedWallAsset;
|
||||||
|
},
|
||||||
|
|
||||||
setWallAssetPosition: (uuid, position) => set(state => {
|
setWallAssetPosition: (uuid, position) => set(state => {
|
||||||
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
||||||
@@ -45,9 +50,17 @@ export const createWallAssetStore = () => {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
removeWallAsset: (uuid) => set(state => {
|
removeWallAsset: (uuid) => {
|
||||||
|
let removedAsset: WallAsset | undefined;
|
||||||
|
set(state => {
|
||||||
|
const asset = state.wallAssets.find(a => a.modelUuid === uuid);
|
||||||
|
if (asset) {
|
||||||
|
removedAsset = JSON.parse(JSON.stringify(asset));
|
||||||
state.wallAssets = state.wallAssets.filter(a => a.modelUuid !== uuid);
|
state.wallAssets = state.wallAssets.filter(a => a.modelUuid !== uuid);
|
||||||
}),
|
}
|
||||||
|
});
|
||||||
|
return removedAsset;
|
||||||
|
},
|
||||||
|
|
||||||
clearWallAssets: () => {
|
clearWallAssets: () => {
|
||||||
set(state => {
|
set(state => {
|
||||||
|
|||||||
Reference in New Issue
Block a user