feat: Add voice and video stage functionality with multi-platform project setup.
Some checks failed
Build and Release / build-and-release (push) Has been cancelled
Some checks failed
Build and Release / build-and-release (push) Has been cancelled
This commit is contained in:
@@ -57,6 +57,13 @@ export const VoiceProvider = ({ children }) => {
|
||||
const [room, setRoom] = useState(null);
|
||||
const [token, setToken] = useState(null);
|
||||
const [activeSpeakers, setActiveSpeakers] = useState(new Set());
|
||||
const speakerRemovalTimers = useRef(new Map());
|
||||
const clearSpeakerTimers = useCallback(() => {
|
||||
for (const timer of speakerRemovalTimers.current.values()) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
speakerRemovalTimers.current.clear();
|
||||
}, []);
|
||||
const [isMuted, setIsMuted] = useState(false);
|
||||
const [isDeafened, setIsDeafened] = useState(false);
|
||||
const [isScreenSharing, setIsScreenSharingLocal] = useState(false);
|
||||
@@ -121,7 +128,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
// Apply volume to LiveKit participant (factoring in global output volume)
|
||||
const participant = room?.remoteParticipants?.get(userId);
|
||||
const globalVol = globalOutputVolume / 100;
|
||||
if (participant) participant.setVolume((volume / 100) * globalVol);
|
||||
if (participant) participant.setVolume(Math.min(2, (volume / 100) * globalVol));
|
||||
// Sync personal mute state
|
||||
if (volume === 0) {
|
||||
setPersonallyMutedUsers(prev => {
|
||||
@@ -155,7 +162,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
const vol = userVolumes[userId] ?? 100;
|
||||
const restoreVol = vol === 0 ? 100 : vol;
|
||||
const participant = room?.remoteParticipants?.get(userId);
|
||||
if (participant) participant.setVolume((restoreVol / 100) * globalVol);
|
||||
if (participant) participant.setVolume(Math.min(2, (restoreVol / 100) * globalVol));
|
||||
// Update stored volume if it was 0
|
||||
if (vol === 0) {
|
||||
setUserVolumes(p => {
|
||||
@@ -269,6 +276,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
const isMobile = /Android|iPhone|iPad/i.test(navigator.userAgent);
|
||||
|
||||
const newRoom = new Room({
|
||||
webAudioMix: true,
|
||||
adaptiveStream: true,
|
||||
dynacast: true,
|
||||
autoSubscribe: true,
|
||||
@@ -347,6 +355,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
if (isMovingRef.current) {
|
||||
setRoom(null);
|
||||
setToken(null);
|
||||
clearSpeakerTimers();
|
||||
setActiveSpeakers(new Set());
|
||||
setConnectionQualities({});
|
||||
return;
|
||||
@@ -357,6 +366,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
console.log('Token expired, auto-reconnecting...');
|
||||
setRoom(null);
|
||||
setToken(null);
|
||||
clearSpeakerTimers();
|
||||
setActiveSpeakers(new Set());
|
||||
setConnectionQualities({});
|
||||
try {
|
||||
@@ -373,6 +383,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
setActiveChannelId(null);
|
||||
setRoom(null);
|
||||
setToken(null);
|
||||
clearSpeakerTimers();
|
||||
setActiveSpeakers(new Set());
|
||||
setConnectionQualities({});
|
||||
|
||||
@@ -384,7 +395,42 @@ export const VoiceProvider = ({ children }) => {
|
||||
});
|
||||
|
||||
newRoom.on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
|
||||
setActiveSpeakers(new Set(speakers.map(p => p.identity)));
|
||||
const currentSpeakerIds = new Set(speakers.map(p => p.identity));
|
||||
|
||||
// Cancel pending removal timers for anyone who is speaking again
|
||||
for (const id of currentSpeakerIds) {
|
||||
const timer = speakerRemovalTimers.current.get(id);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
speakerRemovalTimers.current.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
setActiveSpeakers(prev => {
|
||||
const next = new Set(prev);
|
||||
|
||||
// Add new speakers immediately
|
||||
for (const id of currentSpeakerIds) {
|
||||
next.add(id);
|
||||
}
|
||||
|
||||
// Schedule delayed removal for speakers no longer in the event
|
||||
for (const id of prev) {
|
||||
if (!currentSpeakerIds.has(id) && !speakerRemovalTimers.current.has(id)) {
|
||||
const timer = setTimeout(() => {
|
||||
speakerRemovalTimers.current.delete(id);
|
||||
setActiveSpeakers(s => {
|
||||
const updated = new Set(s);
|
||||
updated.delete(id);
|
||||
return updated;
|
||||
});
|
||||
}, 300);
|
||||
speakerRemovalTimers.current.set(id, timer);
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
});
|
||||
});
|
||||
|
||||
newRoom.on(RoomEvent.Reconnecting, () => {
|
||||
@@ -507,7 +553,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
participant.setVolume(0);
|
||||
} else {
|
||||
const userVol = (userVolumes[identity] ?? 100) / 100;
|
||||
participant.setVolume(userVol * globalVol);
|
||||
participant.setVolume(Math.min(2, userVol * globalVol));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user