diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx
index b80fd53..9abf096 100644
--- a/app/src/components/layout/sidebarRight/Header.tsx
+++ b/app/src/components/layout/sidebarRight/Header.tsx
@@ -1,14 +1,18 @@
-import React from "react";
+import React, { useEffect } from "react";
import { AppDockIcon } from "../../icons/HeaderIcons";
-import orgImg from "../../../assets/orgTemp.png"
+import orgImg from "../../../assets/orgTemp.png";
+import { useActiveUsers } from "../../../store/store";
+import { getAvatarColor } from "../../../modules/collaboration/users/functions/getAvatarColor";
+import { ActiveUser } from "../../../types/users";
const Header: React.FC = () => {
- const guestUsers = [
- { value: "Nazria", color: "#43C06D" },
- { value: "Name1", color: "#0050EB" },
- { value: "Abigail", color: "#FF6600" },
- { value: "Jack", color: "#488EF6" },
- ]; // Example guest users array
+ const { activeUsers } = useActiveUsers();
+ const userName = localStorage.getItem("userName") || "Anonymous";
+
+ const guestUsers: ActiveUser[] = activeUsers.filter(
+ (user: ActiveUser) => user.userName !== userName
+ );
+ console.log('guestUsers: ', guestUsers);
return (
@@ -25,18 +29,23 @@ const Header: React.FC = () => {
+{guestUsers.length - 3}
)}
{guestUsers.slice(0, 3).map((user, index) => (
-
- {user.value[0]}
-
+ <>
+
+ {user.userName[0]}
+
+ >
))}
-
- V
+
+ {userName[0]}

diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx
index 15e8bda..c93f275 100644
--- a/app/src/components/ui/componets/DisplayZone.tsx
+++ b/app/src/components/ui/componets/DisplayZone.tsx
@@ -144,7 +144,7 @@ const DisplayZone: React.FC
= ({
return (
diff --git a/app/src/modules/collaboration/collabCams.tsx b/app/src/modules/collaboration/collabCams.tsx
index da20b34..16e0e29 100644
--- a/app/src/modules/collaboration/collabCams.tsx
+++ b/app/src/modules/collaboration/collabCams.tsx
@@ -1,112 +1,169 @@
-import * as THREE from 'three';
-import { useEffect, useRef, useState } from 'react';
-import { useFrame } from '@react-three/fiber';
-import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
-import camModel from '../../assets/gltf-glb/camera face 2.gltf';
-import getActiveUsersData from '../../services/factoryBuilder/collab/getActiveUsers';
-import { useActiveUsers, useSocketStore } from '../../store/store';
-import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
-import { useNavigate } from 'react-router-dom';
-import { Text, Html } from '@react-three/drei';
-import CollabUserIcon from './collabUserIcon';
-import image from '../../assets/image/userImage.png';
-
+import * as THREE from "three";
+import { useEffect, useRef, useState } from "react";
+import { useFrame } from "@react-three/fiber";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
+import camModel from "../../assets/gltf-glb/camera face 2.gltf";
+import getActiveUsersData from "../../services/factoryBuilder/collab/getActiveUsers";
+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 "./collabUserIcon";
+import { getAvatarColor } from "./users/functions/getAvatarColor";
const CamModelsGroup = () => {
let navigate = useNavigate();
const groupRef = useRef
(null);
- const email = localStorage.getItem('email');
+ const email = localStorage.getItem("email");
const { activeUsers, setActiveUsers } = useActiveUsers();
const { socket } = useSocketStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
const [cams, setCams] = useState([]);
- const [models, setModels] = useState>({});
+ const [models, setModels] = useState<
+ Record<
+ string,
+ { targetPosition: THREE.Vector3; targetRotation: THREE.Euler }
+ >
+ >({});
- dracoLoader.setDecoderPath('three/examples/jsm/libs/draco/gltf/');
+ dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
useEffect(() => {
if (!email) {
- navigate('/');
+ navigate("/");
}
if (!socket) return;
- const organization = email!.split('@')[1].split('.')[0];
+ const organization = email!.split("@")[1].split(".")[0];
- socket.on('userConnectRespones', (data: any) => {
+ socket.on("userConnectRespones", (data: any) => {
if (!groupRef.current) return;
- if (data.data.userData.email === email) return
- if (socket.id === data.socketId || organization !== data.organization) return;
+ if (data.data.userData.email === email) return;
+ if (socket.id === data.socketId || organization !== data.organization)
+ return;
- const model = groupRef.current.getObjectByProperty('uuid', data.data.userData._id);
+ const model = groupRef.current.getObjectByProperty(
+ "uuid",
+ data.data.userData._id
+ );
if (model) {
groupRef.current.remove(model);
}
loader.load(camModel, (gltf) => {
const newModel = gltf.scene.clone();
newModel.uuid = data.data.userData._id;
- newModel.position.set(data.data.position.x, data.data.position.y, data.data.position.z);
- newModel.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z);
+ newModel.position.set(
+ data.data.position.x,
+ data.data.position.y,
+ data.data.position.z
+ );
+ newModel.rotation.set(
+ data.data.rotation.x,
+ data.data.rotation.y,
+ data.data.rotation.z
+ );
newModel.userData = data.data.userData;
setCams((prev) => [...prev, newModel]);
setActiveUsers([...activeUsers, data.data.userData]);
});
});
- socket.on('userDisConnectRespones', (data: any) => {
+ socket.on("userDisConnectRespones", (data: any) => {
if (!groupRef.current) return;
- if (socket.id === data.socketId || organization !== data.organization) return;
+ if (socket.id === data.socketId || organization !== data.organization)
+ return;
- setCams((prev) => prev.filter((cam) => cam.uuid !== data.data.userData._id));
- setActiveUsers(activeUsers.filter((user: any) => user._id !== data.data.userData._id));
+ setCams((prev) =>
+ prev.filter((cam) => cam.uuid !== data.data.userData._id)
+ );
+ setActiveUsers(
+ activeUsers.filter((user: any) => user._id !== data.data.userData._id)
+ );
});
- socket.on('cameraUpdateResponse', (data: any) => {
- if (!groupRef.current || socket.id === data.socketId || organization !== data.organization) return;
+ socket.on("cameraUpdateResponse", (data: any) => {
+ if (
+ !groupRef.current ||
+ socket.id === data.socketId ||
+ organization !== data.organization
+ )
+ return;
setModels((prev) => ({
...prev,
[data.data.userId]: {
- targetPosition: new THREE.Vector3(data.data.position.x, data.data.position.y, data.data.position.z),
- targetRotation: new THREE.Euler(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z),
+ targetPosition: new THREE.Vector3(
+ data.data.position.x,
+ data.data.position.y,
+ data.data.position.z
+ ),
+ targetRotation: new THREE.Euler(
+ data.data.rotation.x,
+ data.data.rotation.y,
+ data.data.rotation.z
+ ),
},
}));
});
return () => {
- socket.off('userConnectRespones');
- socket.off('userDisConnectRespones');
- socket.off('cameraUpdateResponse');
+ socket.off("userConnectRespones");
+ socket.off("userDisConnectRespones");
+ socket.off("cameraUpdateResponse");
};
}, [socket]);
useFrame(() => {
if (!groupRef.current) return;
Object.keys(models).forEach((uuid) => {
- const model = groupRef.current!.getObjectByProperty('uuid', uuid);
+ const model = groupRef.current!.getObjectByProperty("uuid", uuid);
if (!model) return;
const { targetPosition, targetRotation } = models[uuid];
model.position.lerp(targetPosition, 0.1);
- model.rotation.x = THREE.MathUtils.lerp(model.rotation.x, targetRotation.x, 0.1);
- model.rotation.y = THREE.MathUtils.lerp(model.rotation.y, targetRotation.y, 0.1);
- model.rotation.z = THREE.MathUtils.lerp(model.rotation.z, targetRotation.z, 0.1);
+ model.rotation.x = THREE.MathUtils.lerp(
+ model.rotation.x,
+ targetRotation.x,
+ 0.1
+ );
+ model.rotation.y = THREE.MathUtils.lerp(
+ model.rotation.y,
+ targetRotation.y,
+ 0.1
+ );
+ model.rotation.z = THREE.MathUtils.lerp(
+ model.rotation.z,
+ targetRotation.z,
+ 0.1
+ );
});
});
useEffect(() => {
if (!groupRef.current) return;
- const organization = email!.split('@')[1].split('.')[0];
+ const organization = email!.split("@")[1].split(".")[0];
getActiveUsersData(organization).then((data) => {
- const filteredData = data.cameraDatas.filter((camera: any) => camera.userData.email !== email);
+ const filteredData = data.cameraDatas.filter(
+ (camera: any) => camera.userData.email !== email
+ );
if (filteredData.length > 0) {
loader.load(camModel, (gltf) => {
const newCams = filteredData.map((cam: any) => {
const newModel = gltf.scene.clone();
newModel.uuid = cam.userData._id;
- newModel.position.set(cam.position.x, cam.position.y, cam.position.z);
- newModel.rotation.set(cam.rotation.x, cam.rotation.y, cam.rotation.z);
+ newModel.position.set(
+ cam.position.x,
+ cam.position.y,
+ cam.position.z
+ );
+ newModel.rotation.set(
+ cam.rotation.x,
+ cam.rotation.y,
+ cam.rotation.z
+ );
newModel.userData = cam.userData;
+ console.log('cam.userData: ', cam.userData);
setActiveUsers([...activeUsers, cam.userData]);
return newModel;
});
@@ -119,7 +176,7 @@ const CamModelsGroup = () => {
return (
{cams.map((cam, index) => (
-
+
{
textAlign: "center",
fontFamily: "Arial, sans-serif",
}}
- position={[-0.015, 0, 0.7]}>
-
+ position={[-0.015, 0, 0.7]}
+ >
+
))}
diff --git a/app/src/modules/collaboration/collabUserIcon.tsx b/app/src/modules/collaboration/collabUserIcon.tsx
index a6c22c0..acc3fa0 100644
--- a/app/src/modules/collaboration/collabUserIcon.tsx
+++ b/app/src/modules/collaboration/collabUserIcon.tsx
@@ -1,53 +1,33 @@
import React from "react";
+import CustomAvatar from "./users/Avatar";
interface CollabUserIconProps {
- color: string;
- userImage: string;
- userName: string;
+ userName: string;
+ userImage?: string;
+ index?: number;
+ color: string;
}
const CollabUserIcon: React.FC = ({
- color,
- userImage,
- userName,
+ userImage,
+ userName,
+ index = 0,
+ color,
}) => {
- return (
-
-

-
- {userName}
-
-
- );
+ return (
+
+
+ {userImage ? (
+

+ ) : (
+
+ )}
+
+
+ {userName}
+
+
+ );
};
export default CollabUserIcon;
diff --git a/app/src/modules/collaboration/users/Avatar.tsx b/app/src/modules/collaboration/users/Avatar.tsx
new file mode 100644
index 0000000..93d45c3
--- /dev/null
+++ b/app/src/modules/collaboration/users/Avatar.tsx
@@ -0,0 +1,59 @@
+import React, { useEffect, useState } from "react";
+import { getInitials } from "./functions/getInitials";
+import { getAvatarColor } from "./functions/getAvatarColor";
+
+interface AvatarProps {
+ name: string; // Name can be a full name or initials
+ size?: number;
+ index?: number;
+ textColor?: string;
+}
+
+const CustomAvatar: React.FC = ({
+ name,
+ size = 100,
+ index = 0,
+ textColor = "#ffffff",
+}) => {
+ const [imageSrc, setImageSrc] = useState(null);
+
+ useEffect(() => {
+ const canvas = document.createElement("canvas"); // Create an offscreen canvas
+ canvas.width = size;
+ canvas.height = size;
+ const ctx = canvas.getContext("2d");
+ if (ctx) {
+ const initials = getInitials(name); // Convert name to initials if needed
+
+ // Draw background
+ ctx.fillStyle = getAvatarColor(index);
+ ctx.fillRect(0, 0, size, size);
+
+ // Draw initials
+ ctx.fillStyle = textColor;
+ ctx.font = `bold ${size / 2}px Arial`;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ ctx.fillText(initials, size / 2, size / 2);
+
+ // Generate image source
+ const dataURL = canvas.toDataURL("image/png");
+ setImageSrc(dataURL);
+ }
+ }, [name, size, textColor]);
+
+ if (!imageSrc) {
+ return null; // Return null while the image is being generated
+ }
+
+ return (
+
+ );
+};
+
+export default CustomAvatar;
diff --git a/app/src/modules/collaboration/users/functions/getAvatarColor.ts b/app/src/modules/collaboration/users/functions/getAvatarColor.ts
new file mode 100644
index 0000000..d3186d2
--- /dev/null
+++ b/app/src/modules/collaboration/users/functions/getAvatarColor.ts
@@ -0,0 +1,26 @@
+const avatarColors: string[] = [
+ "#FF5733", // Red Orange
+ "#48ac2a", // Leaf Green
+ "#0050eb", // Royal Blue
+ "#FF33A1", // Hot Pink
+ "#FF8C33", // Deep Orange
+ "#8C33FF", // Violet
+ "#FF3333", // Bright Red
+ "#43c06d", // Emerald Green
+ "#A133FF", // Amethyst Purple
+ "#C70039", // Crimson
+ "#900C3F", // Maroon
+ "#581845", // Plum
+ "#3498DB", // Sky Blue
+ "#2ECC71", // Green Mint
+ "#E74C3C", // Tomato Red
+ "#00adff", // Azure
+ "#DBAD05", // Amber Yellow
+ "#FF5733", // Red Orange
+ "#FF33A1", // Hot Pink
+ "#900C3F", // Maroon
+];
+
+export function getAvatarColor(index: number): string {
+ return avatarColors[index % avatarColors.length];
+}
diff --git a/app/src/modules/collaboration/users/functions/getInitials.ts b/app/src/modules/collaboration/users/functions/getInitials.ts
new file mode 100644
index 0000000..5ebaa19
--- /dev/null
+++ b/app/src/modules/collaboration/users/functions/getInitials.ts
@@ -0,0 +1,10 @@
+export const getInitials = (fullName: string): string => {
+ // Extract initials from the name
+ const words = fullName.split(" ");
+ const initials = words
+ .map((word) => word[0])
+ .slice(0, 2)
+ .join("")
+ .toUpperCase();
+ return initials;
+};
\ No newline at end of file
diff --git a/app/src/styles/layout/popup.scss b/app/src/styles/layout/popup.scss
index a086cf4..20ea3f2 100644
--- a/app/src/styles/layout/popup.scss
+++ b/app/src/styles/layout/popup.scss
@@ -110,3 +110,29 @@
}
}
}
+
+.collab-user-live-container{
+ @include flex-center;
+ flex-direction: column;
+ gap: 6px;
+ .user-image-container{
+ height: 30px;
+ width: 30px;
+ border-radius: #{$border-radius-circle};
+ overflow: hidden;
+ .user-image{
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ vertical-align: top;
+ }
+ }
+ .user-name{
+ padding: 4px 6px;
+ border-radius: #{$border-radius-small};
+ color: white;
+ font-size: var(--font-size-regulaar);
+ font-weight: var(--font-size-regulaar);
+ text-transform: capitalize;
+ }
+}
diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss
index d595b92..b248ad2 100644
--- a/app/src/styles/layout/sidebar.scss
+++ b/app/src/styles/layout/sidebar.scss
@@ -225,11 +225,13 @@
border-radius: 50%;
font-weight: var(--font-weight-bold);
color: white;
+ text-transform: capitalize;
}
.guest-users-container {
display: flex;
-
+ width: 100%;
+ justify-content: flex-end;
.other-guest {
@include flex-center;
height: 26px;
@@ -249,7 +251,8 @@
display: flex;
.user-organization {
- height: 100%;
+ height: 26px;
+ width: 52px;
max-width: 52px;
border-radius: 20px;
overflow: hidden;
diff --git a/app/src/types/users.d.ts b/app/src/types/users.d.ts
index 9e8c35a..55d5361 100644
--- a/app/src/types/users.d.ts
+++ b/app/src/types/users.d.ts
@@ -8,4 +8,11 @@ export interface User {
type AccessOption = {
option: string;
+};
+
+export type ActiveUser = {
+ _id: string;
+ userName: string;
+ email: string;
+ activeStatus?: string; // Optional property
};
\ No newline at end of file