Add Yjs integration for collaborative drawing and update socket handling
This commit is contained in:
@@ -53,7 +53,7 @@ function MainScene() {
|
||||
const { toggleThreeD } = useThreeDStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { widgetSubOption } = useWidgetSubOption();
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
const { socket, visualizationSocket } = useSocketStore();
|
||||
const { selectedZone } = useSelectedZoneStore();
|
||||
const { setFloatingWidget } = useFloatingWidget();
|
||||
const { clearComparisonProduct } = useComparisonProduct();
|
||||
@@ -68,6 +68,24 @@ function MainScene() {
|
||||
const { selectedVersion, setSelectedVersion } = selectedVersionStore();
|
||||
const { selectedComment, commentPositionState } = useSelectedComment();
|
||||
|
||||
useEffect(() => {
|
||||
console.log('hi');
|
||||
if (!projectId || !selectedVersion?.versionId || !socket) return;
|
||||
|
||||
socket.emit("joinRoom", { projectId, versionId: selectedVersion.versionId });
|
||||
|
||||
socket.on("v1:Line-collab:response:create", (data: any) => {
|
||||
console.log("v1:Line-collab:response:create:", data);
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (projectId && selectedVersion?.versionId && socket) {
|
||||
socket.emit("leaveRoom", { projectId, versionId: selectedVersion.versionId });
|
||||
socket.off("v1:Line-collab:response:create");
|
||||
}
|
||||
};
|
||||
}, [projectId, selectedVersion?.versionId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== 'simulation') {
|
||||
clearComparisonProduct();
|
||||
@@ -188,7 +206,7 @@ function MainScene() {
|
||||
{activeModule !== "market" && !selectedUser && <Footer />}
|
||||
|
||||
<VersionSaved />
|
||||
{(commentPositionState !== null || selectedComment !== null) && <ThreadChat/>}
|
||||
{(commentPositionState !== null || selectedComment !== null) && <ThreadChat />}
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -12,6 +12,8 @@ import arrayLineToObject from "./lineConvertions/arrayLineToObject";
|
||||
// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi';
|
||||
import { Socket } from "socket.io-client";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
import * as Y from 'yjs';
|
||||
import { generateUniqueId } from "../../../../functions/generateUniqueId";
|
||||
|
||||
async function drawWall(
|
||||
raycaster: THREE.Raycaster,
|
||||
@@ -37,6 +39,7 @@ async function drawWall(
|
||||
socket: Socket<any>,
|
||||
projectId?: string,
|
||||
versionId?: string,
|
||||
ydoc?: any
|
||||
): Promise<void> {
|
||||
const { userId, organization, email } = getUserData();
|
||||
////////// Creating lines Based on the positions clicked //////////
|
||||
@@ -142,7 +145,45 @@ async function drawWall(
|
||||
};
|
||||
|
||||
console.log('input: ', input);
|
||||
socket.emit("v1:Line:create", input);
|
||||
socket.emit("v1:Line-collab:create", input);
|
||||
|
||||
// ✅ Add to Yjs map (will auto-sync to other clients)
|
||||
const yLine = new Y.Map();
|
||||
yLine.set("organization", organization);
|
||||
yLine.set("layer", data.layer);
|
||||
yLine.set("type", data.type);
|
||||
yLine.set("line", data.line)
|
||||
yLine.set("socketId", socket.id)
|
||||
yLine.set("projectId", projectId);
|
||||
yLine.set("versionId", versionId);
|
||||
yLine.set("userId", userId);
|
||||
|
||||
const yLineArray = new Y.Array();
|
||||
if (!data.line) return
|
||||
for (const point of data.line) {
|
||||
const yPoint = new Y.Map();
|
||||
yPoint.set("uuid", point.uuid);
|
||||
if (point.position) {
|
||||
yPoint.set("position", new Y.Map([
|
||||
["x", point.position.x],
|
||||
["y", point.position.y],
|
||||
["z", point.position.z],
|
||||
]));
|
||||
}
|
||||
yLineArray.push([yPoint]);
|
||||
}
|
||||
yLine.set("line", yLineArray);
|
||||
|
||||
const lineMap = ydoc.getMap("lines");
|
||||
lineMap.observe((event: any) => {
|
||||
console.log('event: ', event);
|
||||
console.log("Map changed!", event.changes.keys);
|
||||
});
|
||||
|
||||
const lineId = crypto.randomUUID(); // Or use backend _id if available
|
||||
lineMap.set(lineId, yLine); // 👈 triggers Yjs update event
|
||||
|
||||
|
||||
|
||||
setNewLines([newLines[0], newLines[1], line.current]);
|
||||
lines.current.push(line.current as Types.Line);
|
||||
@@ -156,6 +197,8 @@ async function drawWall(
|
||||
let lastPoint = line.current[line.current.length - 1];
|
||||
line.current = [lastPoint];
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -203,15 +246,45 @@ async function drawWall(
|
||||
CONSTANTS.lineConfig.wallName,
|
||||
]);
|
||||
|
||||
// if (line.current.length >= 2 && line.current[0] && line.current[1]) {
|
||||
// const data = arrayLineToObject(line.current as Types.Line);
|
||||
|
||||
// //REST
|
||||
|
||||
// // setLine(organization, data.layer!, data.line!, data.type!);
|
||||
|
||||
// //SOCKET
|
||||
|
||||
// const input = {
|
||||
// organization,
|
||||
// layer: data.layer,
|
||||
// line: data.line,
|
||||
// type: data.type,
|
||||
// socketId: socket.id,
|
||||
// versionId,
|
||||
// projectId,
|
||||
// userId,
|
||||
// };
|
||||
|
||||
// console.log('input: ', input);
|
||||
// socket.emit("v1:Line-collab:create", input);
|
||||
|
||||
// setNewLines([line.current]);
|
||||
// lines.current.push(line.current as Types.Line);
|
||||
// addLineToScene(
|
||||
// line.current[0][0],
|
||||
// line.current[1][0],
|
||||
// CONSTANTS.lineConfig.wallColor,
|
||||
// line.current,
|
||||
// floorPlanGroupLine
|
||||
// );
|
||||
// let lastPoint = line.current[line.current.length - 1];
|
||||
// line.current = [lastPoint];
|
||||
// }
|
||||
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
|
||||
const data = arrayLineToObject(line.current as Types.Line);
|
||||
|
||||
//REST
|
||||
|
||||
// setLine(organization, data.layer!, data.line!, data.type!);
|
||||
|
||||
//SOCKET
|
||||
|
||||
// ✅ Prepare input for socket.emit (your backend)
|
||||
const input = {
|
||||
organization,
|
||||
layer: data.layer,
|
||||
@@ -223,9 +296,46 @@ async function drawWall(
|
||||
userId,
|
||||
};
|
||||
|
||||
console.log('input: ', input);
|
||||
socket.emit("v1:Line:create", input);
|
||||
// ✅ Emit to backend
|
||||
socket.emit("v1:Line-collab:create", input);
|
||||
|
||||
// ✅ Add to Yjs map (will auto-sync to other clients)
|
||||
const yLine = new Y.Map();
|
||||
yLine.set("organization", organization);
|
||||
yLine.set("layer", data.layer);
|
||||
yLine.set("type", data.type);
|
||||
yLine.set("line", data.line)
|
||||
yLine.set("socketId", socket.id)
|
||||
yLine.set("projectId", projectId);
|
||||
yLine.set("versionId", versionId);
|
||||
yLine.set("userId", userId);
|
||||
|
||||
const yLineArray = new Y.Array();
|
||||
if (!data.line) return
|
||||
for (const point of data.line) {
|
||||
const yPoint = new Y.Map();
|
||||
yPoint.set("uuid", point.uuid);
|
||||
if (point.position) {
|
||||
yPoint.set("position", new Y.Map([
|
||||
["x", point.position.x],
|
||||
["y", point.position.y],
|
||||
["z", point.position.z],
|
||||
]));
|
||||
}
|
||||
yLineArray.push([yPoint]);
|
||||
}
|
||||
yLine.set("line", yLineArray);
|
||||
|
||||
const lineMap = ydoc.getMap("lines");
|
||||
lineMap.observe((event: any) => {
|
||||
console.log('event: ', event);
|
||||
console.log("Map changed!", event.changes.keys);
|
||||
});
|
||||
|
||||
const lineId = generateUniqueId(); // Or use backend _id if available
|
||||
lineMap.set(lineId, yLine); // 👈 triggers Yjs update event
|
||||
|
||||
// ✅ Scene + local updates
|
||||
setNewLines([line.current]);
|
||||
lines.current.push(line.current as Types.Line);
|
||||
addLineToScene(
|
||||
@@ -235,7 +345,8 @@ async function drawWall(
|
||||
line.current,
|
||||
floorPlanGroupLine
|
||||
);
|
||||
let lastPoint = line.current[line.current.length - 1];
|
||||
|
||||
const lastPoint = line.current[line.current.length - 1];
|
||||
line.current = [lastPoint];
|
||||
}
|
||||
if (isSnapped.current) {
|
||||
|
||||
@@ -31,11 +31,13 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
||||
const { setNewLines } = useNewLines();
|
||||
const { setDeletedLines } = useDeletedLines();
|
||||
const { socket } = useSocketStore();
|
||||
const { ydoc } = useSocketStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { organization } = getUserData();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (toolMode === 'move') {
|
||||
addDragControl(dragPointControls, currentLayerPoint, state, floorPlanGroupPoint, floorPlanGroupLine, lines, onlyFloorlines, socket, projectId, selectedVersion?.versionId || '',);
|
||||
@@ -148,7 +150,7 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
||||
}
|
||||
|
||||
if (toolMode === "Wall") {
|
||||
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',);
|
||||
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '', ydoc);
|
||||
}
|
||||
|
||||
if (toolMode === "Floor") {
|
||||
|
||||
@@ -1,24 +1,73 @@
|
||||
import * as THREE from "three";
|
||||
import { create } from "zustand";
|
||||
import * as Y from 'yjs';
|
||||
import { io } from "socket.io-client";
|
||||
import * as CONSTANTS from "../../types/world/worldConstants";
|
||||
|
||||
export const useSocketStore = create<any>((set: any, get: any) => ({
|
||||
socket: null,
|
||||
ydoc: new Y.Doc(),
|
||||
|
||||
initializeSocket: (email?: string, organization?: string, token?: string) => {
|
||||
const existingSocket = get().socket;
|
||||
if (existingSocket) {
|
||||
return;
|
||||
}
|
||||
if (get().socket) return;
|
||||
|
||||
// const socket = io(
|
||||
// `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`,
|
||||
// {
|
||||
// reconnection: true,
|
||||
// auth: { token },
|
||||
// }
|
||||
// );
|
||||
|
||||
const socket = io(
|
||||
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`,
|
||||
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/BuilderYjs_v1`,
|
||||
{
|
||||
reconnection: true,
|
||||
auth: { token },
|
||||
}
|
||||
);
|
||||
|
||||
const ydoc = get().ydoc;
|
||||
socket.on('v1:Line-collab:response:create', (update) => {
|
||||
try {
|
||||
console.log('socket: ', update);
|
||||
const base64Update = update?.data?.update;
|
||||
// console.log('base64Update: ', base64Update);
|
||||
if (!base64Update) return;
|
||||
|
||||
// Decode base64 to Uint8Array
|
||||
const binaryString = atob(base64Update);
|
||||
// console.log('binaryString: ', binaryString);
|
||||
const binaryUpdate = new Uint8Array(binaryString.length);
|
||||
console.log('binaryUpdate: ', binaryUpdate);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
binaryUpdate[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
|
||||
console.log('binaryUpdate:', binaryUpdate);
|
||||
const isUintArray = binaryUpdate instanceof Uint8Array;
|
||||
console.log('isUintArray:', isUintArray);
|
||||
|
||||
if (isUintArray) {
|
||||
Y.applyUpdate(ydoc, binaryUpdate); // ✅ apply decoded update
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error applying update:', err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
socket.on("line:add", (data) => {
|
||||
console.log("📥 received line:add", data); // <- Add this
|
||||
// log(➕ line:add\n${JSON.stringify(data, null, 2)});
|
||||
});
|
||||
|
||||
// Broadcast local changes
|
||||
ydoc.on('update', (update: any) => {
|
||||
console.log('ydoc: ', update);
|
||||
socket.emit('v1:Line-collab:create', update);
|
||||
});
|
||||
|
||||
const visualizationSocket = io(
|
||||
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization_v1`,
|
||||
{
|
||||
@@ -51,6 +100,7 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
|
||||
|
||||
set({
|
||||
socket,
|
||||
ydoc,
|
||||
visualizationSocket,
|
||||
dashBoardSocket,
|
||||
projectSocket,
|
||||
|
||||
Reference in New Issue
Block a user