) : (
diff --git a/src/components/room/grid/peer-tile.tsx b/src/components/room/grid/peer-tile.tsx
index 0b26cf7..face2a6 100644
--- a/src/components/room/grid/peer-tile.tsx
+++ b/src/components/room/grid/peer-tile.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useRef } from 'react';
import { Mic, MicOff } from 'lucide-react';
import type { Layout } from '@/types';
-import { getInitials } from '@/lib/utils';
+import { cn, getInitials } from '@/lib/utils';
import { usePeerMediasById, usePeerOthersById } from '@/store/conf/hooks';
import { useMedia } from '@/hooks/use-media';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
@@ -48,14 +48,21 @@ export const PeerTile: React.FC
= ({ peerId, layout }) => {
) : (
{/* */}
-
+
{getInitials(peerData.name)}
)}
{/* Mic Status */}
-
+
{media?.mic ? (
) : (
diff --git a/src/components/room/hand.tsx b/src/components/room/hand.tsx
index d32a20b..fc6f8cb 100644
--- a/src/components/room/hand.tsx
+++ b/src/components/room/hand.tsx
@@ -12,11 +12,12 @@ const Hand = () => {
variant="ghost"
size="icon"
className={cn(
- 'w-12 h-12 rounded-xl transition-all duration-200 bg-gradient-to-bl text-white',
+ 'w-12 h-12 rounded-xl transition-all duration-200 bg-linear-to-bl text-white',
isHandRaised
- ? 'bg-yellow-600 hover:bg-yellow-700 '
+ ? 'from-white/10 hover:from-white/15 '
: ' from-white/15 to-white/1 backdrop-blur-xl'
)}
+ title="Coming soon"
>
diff --git a/src/components/room/header.tsx b/src/components/room/header.tsx
index 1292091..75703b9 100644
--- a/src/components/room/header.tsx
+++ b/src/components/room/header.tsx
@@ -1,25 +1,13 @@
-import { Loader2 } from 'lucide-react';
-import { Badge } from '../ui/badge';
-
const Header = () => {
return (
-
- {/* Left side - Meeting info */}
-
-
- {/*
*/}
-
- {/*
*/}
- {/*
Mitsi */}
-
-
+
+ {/*

*/}
- {/* Right side - Recording indicator */}
);
diff --git a/src/components/room/join/join-camera-preview.tsx b/src/components/room/join/join-camera-preview.tsx
index f85701d..5f5dc6c 100644
--- a/src/components/room/join/join-camera-preview.tsx
+++ b/src/components/room/join/join-camera-preview.tsx
@@ -35,18 +35,9 @@ const CameraPreview = () => {
/>
) : (
- {/* Enhanced avatar container with animated background */}
- {/* Animated glow rings */}
-
-
-
- {/* Main avatar */}
-
+
-
- {/* Floating particles around avatar */}
- {/*
*/}
@@ -74,11 +65,6 @@ const CameraPreview = () => {
-
- {/* Floating elements around video container */}
-
- {/*
*/}
- {/*
*/}
);
};
diff --git a/src/components/room/menu.tsx b/src/components/room/menu.tsx
index 82e929d..526dae1 100644
--- a/src/components/room/menu.tsx
+++ b/src/components/room/menu.tsx
@@ -7,8 +7,9 @@ const Menu = () => {
variant="ghost"
size="icon"
className="w-12 h-12 rounded-xl text-white
- bg-gradient-to-tl from-white/15 to-white/1 backdrop-blur-xl
+ bg-linear-to-tl from-white/15 to-white/1 backdrop-blur-xl
"
+ title="Coming soon"
>
diff --git a/src/components/room/sidebar.tsx b/src/components/room/sidebar.tsx
index e809cbe..1b1a297 100644
--- a/src/components/room/sidebar.tsx
+++ b/src/components/room/sidebar.tsx
@@ -25,7 +25,7 @@ const Sidebar = () => {
return (
diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..a1b647f
--- /dev/null
+++ b/src/components/ui/sonner.tsx
@@ -0,0 +1,38 @@
+import {
+ CircleCheckIcon,
+ InfoIcon,
+ Loader2Icon,
+ OctagonXIcon,
+ TriangleAlertIcon,
+} from 'lucide-react';
+import { useTheme } from 'next-themes';
+import { Toaster as Sonner, type ToasterProps } from 'sonner';
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = 'system' } = useTheme();
+
+ return (
+ ,
+ info: ,
+ warning: ,
+ error: ,
+ loading: ,
+ }}
+ style={
+ {
+ '--normal-bg': 'var(--popover)',
+ '--normal-text': 'var(--popover-foreground)',
+ '--normal-border': 'var(--border)',
+ '--border-radius': 'var(--radius)',
+ } as React.CSSProperties
+ }
+ {...props}
+ />
+ );
+};
+
+export { Toaster };
diff --git a/src/hooks/use-media.ts b/src/hooks/use-media.ts
index 70585d6..3b03469 100644
--- a/src/hooks/use-media.ts
+++ b/src/hooks/use-media.ts
@@ -163,6 +163,16 @@ export const useMedia = () => {
});
}, [mediaService, signalingService]);
+ const produceUserMedia = useCallback(async () => {
+ if (roomAccess != Access.Allowed) return;
+ if (micOn) await createProducer('mic');
+ if (cameraOn) await createProducer('camera');
+ if (screenOn) {
+ await createProducer('screen');
+ await createProducer('screenAudio');
+ }
+ }, [roomAccess, micOn, cameraOn, screenOn, createProducer]);
+
const closeAllProducers = useCallback(() => {
if (!mediaService) throw new Error('MediaService not initialized');
mediaService.closeAllProducers();
@@ -425,6 +435,7 @@ export const useMedia = () => {
resumeConsumer,
closeConsumer,
createWebRtcConnections,
+ produceUserMedia,
closeAllProducers,
closeAllConsumers,
closeAllTransports,
diff --git a/src/providers/room-provider.tsx b/src/providers/room-provider.tsx
index 48d6d4b..61eb524 100644
--- a/src/providers/room-provider.tsx
+++ b/src/providers/room-provider.tsx
@@ -2,26 +2,28 @@ import { useMedia } from '@/hooks/use-media';
import { useRoom } from '@/hooks/use-room';
import { useSignaling } from '@/hooks/use-signaling';
import { HEARTBEAT_INTERVAL } from '@/lib/constants';
-import { useToastActions } from '@/packages/toast';
import { useRoomAccess, useRoomActions } from '@/store/conf/hooks';
import { Access, type AckCallbackData, type MessageData } from '@/types';
import { Actions } from '@/types/actions';
import { useEffect, useRef, useState, type ReactNode } from 'react';
+import { toast } from 'sonner';
const RoomProvider = ({ children }: { children: ReactNode }) => {
const { signalingService, sendHeartBeat } = useSignaling();
- const toastActions = useToastActions();
const {
mediaService,
createWebRtcConnections,
closeAllConsumers,
closeAllTransports,
closeAllProducers,
+ produceUserMedia,
} = useMedia();
+ const reconnectionToastRef = useRef(0);
+
const heartBeatIntervalRef = useRef(null);
const roomAccess = useRoomAccess();
const roomActions = useRoomActions();
- const [rejoining, setrejoining] = useState(false);
+ const [rejoining, setRejoining] = useState(false);
const { joinRoom, leaveRoom, actionHandlers } = useRoom();
useEffect(() => {
@@ -54,6 +56,7 @@ const RoomProvider = ({ children }: { children: ReactNode }) => {
(async () => {
await joinRoom();
await createWebRtcConnections();
+ await produceUserMedia();
// register heartbeat interval
heartBeatIntervalRef.current = setInterval(
sendHeartBeat,
@@ -67,20 +70,35 @@ const RoomProvider = ({ children }: { children: ReactNode }) => {
if (heartBeatIntervalRef.current)
clearInterval(heartBeatIntervalRef.current);
};
- }, [roomAccess, createWebRtcConnections, joinRoom, sendHeartBeat, leaveRoom]);
+ }, [
+ roomAccess,
+ createWebRtcConnections,
+ produceUserMedia,
+ joinRoom,
+ sendHeartBeat,
+ leaveRoom,
+ ]);
useEffect(() => {
if (roomAccess !== Access.Allowed) return;
if (!rejoining) return;
(async () => {
- closeAllProducers();
+ // closeAllProducers();
closeAllConsumers();
closeAllTransports();
await joinRoom(rejoining);
await createWebRtcConnections();
+ await produceUserMedia();
+ setRejoining(false);
+
+ if (reconnectionToastRef.current)
+ toast.dismiss(reconnectionToastRef.current);
- setrejoining(false);
+ toast.success('You are reconnected', {
+ closeButton: true,
+ richColors: true,
+ });
})().catch(err => console.log(err));
}, [
rejoining,
@@ -91,8 +109,15 @@ const RoomProvider = ({ children }: { children: ReactNode }) => {
closeAllConsumers,
closeAllTransports,
closeAllProducers,
+ produceUserMedia,
]);
+ // produce on join
+ useEffect(() => {
+ if (roomAccess !== Access.Allowed) return;
+ if (rejoining) return;
+ }, [roomAccess, rejoining]);
+
useEffect(() => {
if (!signalingService) return;
if (roomAccess !== Access.Allowed) return;
@@ -100,22 +125,24 @@ const RoomProvider = ({ children }: { children: ReactNode }) => {
const connection = signalingService.getConnection();
// TODO - NEXT LINE OF ACTIONS
connection.on('disconnect', () => {
- console.log('Disconnected from signaling server');
if (connection.active) {
+ reconnectionToastRef.current = toast.loading(
+ 'You are disconnected, attempting to reconnect',
+ {
+ position: 'top-center',
+ richColors: true,
+ }
+ );
// Attempt to reconnect
- toastActions.error('Reconnecting');
- console.log('Attempting to reconnect to signaling server...');
roomActions.setReconnecting(true);
} else {
- console.log('Connection is not active. Will not attempt to reconnect.');
roomActions.setDisconnected(true);
}
});
connection.io.on('reconnect', () => {
- console.log('Reconnected to signaling server');
roomActions.setReconnecting(false);
- setrejoining(true);
+ setRejoining(true);
});
return () => {
// Cleanup on unmount
diff --git a/src/services/media-service.ts b/src/services/media-service.ts
index 31aa5d9..de9d2cd 100644
--- a/src/services/media-service.ts
+++ b/src/services/media-service.ts
@@ -224,11 +224,13 @@ class MediaService {
throw new Error('Send transport not initialized');
}
let producer: mediasoupTypes.Producer;
- const clonedTrack = this.getTrack(source);
+ const track = this.getTrack(source);
- if (!clonedTrack)
+ if (!track)
throw `${source} track was not found -- start ${source} before you create producer`;
+ const clonedTrack = track.clone();
+
if (source === 'camera') {
// Simulcast encoding settings
const encodings = [