feat: Implement collaboration features including user following and avatar management

This commit is contained in:
2025-04-29 12:50:14 +05:30
parent ea53af62c4
commit c1a7fe3015
16 changed files with 243 additions and 63 deletions

View File

@@ -8,9 +8,9 @@ import { useActiveUsers, useSocketStore } from "../../../store/store";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { useNavigate } from "react-router-dom";
import { Html } from "@react-three/drei";
import CollabUserIcon from "../../../functions/collabUserIcon";
import { getAvatarColor } from "../../../functions/users/functions/getAvatarColor";
import CollabUserIcon from "./collabUserIcon";
import useModuleStore from "../../../store/useModuleStore";
import { getAvatarColor } from "../functions/getAvatarColor";
const CamModelsGroup = () => {
const navigate = useNavigate();
@@ -20,13 +20,14 @@ const CamModelsGroup = () => {
const { socket } = useSocketStore();
const { activeModule } = useModuleStore();
// eslint-disable-next-line react-hooks/exhaustive-deps
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
const [cams, setCams] = useState<any[]>([]);
const [models, setModels] = useState<Record<string, { targetPosition: THREE.Vector3; targetRotation: THREE.Euler }>>({});
const [models, setModels] = useState<Record<string, { targetPosition: THREE.Vector3; targetRotation: THREE.Euler, target: THREE.Vector3 }>>({});
const dedupeCams = (cams: any[]) => {
const seen = new Set();
@@ -102,6 +103,7 @@ const CamModelsGroup = () => {
});
socket.on("cameraUpdateResponse", (data: any) => {
if (
!groupRef.current ||
socket.id === data.socketId ||
@@ -122,6 +124,11 @@ const CamModelsGroup = () => {
data.data.rotation.y,
data.data.rotation.z
),
target: new THREE.Vector3(
data.data.target.x,
data.data.target.y,
data.data.target.z
),
},
}));
});
@@ -131,7 +138,7 @@ const CamModelsGroup = () => {
socket.off("userDisConnectResponse");
socket.off("cameraUpdateResponse");
};
}, [socket]);
}, [email, loader, navigate, setActiveUsers, socket]);
useFrame(() => {
if (!groupRef.current) return;
@@ -217,9 +224,11 @@ const CamModelsGroup = () => {
position={[-0.015, 0, 0.7]}
>
<CollabUserIcon
userImage={cam.userData.userImage || ""}
userImage={cam.userData.userImage ?? ""}
userName={cam.userData.userName}
color={getAvatarColor(index, cam.userData.userName)}
position={cam.position}
rotation={cam.rotation}
/>
</Html>
</primitive>

View File

@@ -0,0 +1,56 @@
import React from "react";
import CustomAvatar from "../users/Avatar";
import { useSelectedUserStore } from "../../../store/useCollabStore";
import { useCamMode } from "../../../store/store";
interface CollabUserIconProps {
userName: string;
userImage?: string;
color: string;
position?: {
x: number;
y: number;
z: number;
};
rotation?: {
x: number;
y: number;
z: number;
};
}
const CollabUserIcon: React.FC<CollabUserIconProps> = ({
userImage,
userName,
color,
position,
rotation,
}) => {
const { setSelectedUser } = useSelectedUserStore();
const { setCamMode } = useCamMode();
return (
<div className="collab-user-live-container">
<button
className="user-image-container"
onClick={() => {
if(!position || !rotation) return;
// Set the selected user in the store
setSelectedUser({ color: color, name: userName, location: { position, rotation } });
setCamMode("FollowPerson");
}}
>
{userImage ? (
<img className="user-image" src={userImage} alt={userName} />
) : (
<CustomAvatar name={userName} color={color} />
)}
</button>
<div className="user-name" style={{ backgroundColor: color }}>
{userName}
</div>
</div>
);
};
export default CollabUserIcon;