Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,19 @@
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@vitejs/plugin-react": "^6.0.3",
"@vitejs/plugin-react": "^6.1.2",
"eslint": "catalog:",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.3",
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.1",
"eslint-plugin-react-refresh": "^0.5.7",
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.2",
"eslint-plugin-unused-imports": "catalog:",
"globals": "catalog:",
"prettier": "catalog:",
"prettier-plugin-organize-imports": "catalog:",
"tw-animate-css": "^1.4.0",
"typescript": "catalog:",
"typescript-eslint": "catalog:",
"vite": "^8.1.3",
"vite": "^8.3.3",
"vitest": "catalog:"
}
}
48 changes: 48 additions & 0 deletions apps/client/src/components/devices-provider/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { MICROPHONE_GATE_DEFAULT_THRESHOLD_DB } from '@/helpers/audio-gate';
import {
NoiseSuppressionMode,
Resolution,
VideoCodec,
type TDeviceSettings
} from '@/types';
import { DEFAULT_BITRATE, DEFAULT_WEBCAM_BITRATE } from '@caesar/shared';
import { createContext } from 'react';

const DEFAULT_DEVICE_SETTINGS: TDeviceSettings = {
microphoneId: undefined,
playbackId: undefined,
webcamId: undefined,
webcamResolution: Resolution['720p'],
webcamFramerate: 30,
webcamBitrate: DEFAULT_WEBCAM_BITRATE,
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
noiseGateEnabled: true,
noiseGateThresholdDb: MICROPHONE_GATE_DEFAULT_THRESHOLD_DB,
noiseSuppressionMode: NoiseSuppressionMode.RNNOISE,
pushToTalkEnabled: false,
pushToTalkKeys: 'Space',
shareSystemAudio: true,
mirrorOwnVideo: false,
simulcastEnabled: true,
screenResolution: Resolution['720p'],
screenFramerate: 30,
screenCodec: VideoCodec.AUTO,
screenBitrate: DEFAULT_BITRATE
};

type TDevicesProvider = {
loading: boolean;
devices: TDeviceSettings;
saveDevices: (newDevices: TDeviceSettings) => void;
};

const DevicesProviderContext = createContext<TDevicesProvider>({
loading: false,
devices: DEFAULT_DEVICE_SETTINGS,
saveDevices: () => {}
});

export { DEFAULT_DEVICE_SETTINGS, DevicesProviderContext };
export type { TDevicesProvider };
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext } from 'react';
import { DevicesProviderContext } from '..';
import { DevicesProviderContext } from '../context';

const useDevices = () => {
const context = useContext(DevicesProviderContext);
Expand Down
60 changes: 7 additions & 53 deletions apps/client/src/components/devices-provider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,17 @@
import { MICROPHONE_GATE_DEFAULT_THRESHOLD_DB } from '@/helpers/audio-gate';
import {
getLocalStorageItemAsJSON,
LocalStorageKey,
setLocalStorageItemAsJSON
} from '@/helpers/storage';
import { NoiseSuppressionMode, type TDeviceSettings } from '@/types';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
NoiseSuppressionMode,
Resolution,
VideoCodec,
type TDeviceSettings
} from '@/types';
import { DEFAULT_BITRATE, DEFAULT_WEBCAM_BITRATE } from '@caesar/shared';
import {
createContext,
memo,
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
DEFAULT_DEVICE_SETTINGS,
DevicesProviderContext,
type TDevicesProvider
} from './context';
import { useAvailableDevices } from './hooks/use-available-devices';

const DEFAULT_DEVICE_SETTINGS: TDeviceSettings = {
microphoneId: undefined,
playbackId: undefined,
webcamId: undefined,
webcamResolution: Resolution['720p'],
webcamFramerate: 30,
webcamBitrate: DEFAULT_WEBCAM_BITRATE,
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
noiseGateEnabled: true,
noiseGateThresholdDb: MICROPHONE_GATE_DEFAULT_THRESHOLD_DB,
noiseSuppressionMode: NoiseSuppressionMode.RNNOISE,
pushToTalkEnabled: false,
pushToTalkKeys: 'Space',
shareSystemAudio: true,
mirrorOwnVideo: false,
simulcastEnabled: true,
screenResolution: Resolution['720p'],
screenFramerate: 30,
screenCodec: VideoCodec.AUTO,
screenBitrate: DEFAULT_BITRATE
};

export type TDevicesProvider = {
loading: boolean;
devices: TDeviceSettings;
saveDevices: (newDevices: TDeviceSettings) => void;
};

const DevicesProviderContext = createContext<TDevicesProvider>({
loading: false,
devices: DEFAULT_DEVICE_SETTINGS,
saveDevices: () => {}
});

type TDevicesProviderProps = {
children: React.ReactNode;
};
Expand Down Expand Up @@ -158,4 +112,4 @@ const DevicesProvider = memo(({ children }: TDevicesProviderProps) => {
);
});

export { DevicesProvider, DevicesProviderContext };
export { DevicesProvider };
3 changes: 1 addition & 2 deletions apps/client/src/components/left-sidebar/categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,7 @@ const Categories = memo(() => {
active.data.current?.userId ?? aId.slice(3)
);
const sourceChannelId = active.data.current?.channelId as
| number
| undefined;
number | undefined;
const destChannelId = Number(oId.slice(3));

if (destChannelId === sourceChannelId) return;
Expand Down
1 change: 0 additions & 1 deletion apps/client/src/components/left-sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,4 @@ const LeftSidebar = memo(({ className, isOpen = true }: TLeftSidebarProps) => {
);
});

export { UserControl } from './user-control';
export { LeftSidebar };
128 changes: 128 additions & 0 deletions apps/client/src/components/media-provider/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import type { TRemoteUserStreamKinds } from '@/types';
import type {
StreamKind,
TStreamQuality,
TStreamQualityLayer,
TVoiceUserState
} from '@caesar/shared';
import type { RtpCapabilities } from 'mediasoup-client/types';
import { createContext } from 'react';
import type { useLocalStreams } from './hooks/use-local-streams';
import type { useRemoteStreams } from './hooks/use-remote-streams';
import type { TransportStatsData } from './hooks/use-transport-stats';
import type { useVoiceControls } from './hooks/use-voice-controls';

type AudioVideoRefs = {
videoRef: React.RefObject<HTMLVideoElement | null>;
audioRef: React.RefObject<HTMLAudioElement | null>;
screenShareRef: React.RefObject<HTMLVideoElement | null>;
screenShareAudioRef: React.RefObject<HTMLAudioElement | null>;
externalAudioRef: React.RefObject<HTMLAudioElement | null>;
externalVideoRef: React.RefObject<HTMLVideoElement | null>;
};

enum ConnectionStatus {
DISCONNECTED = 'disconnected',
CONNECTING = 'connecting',
CONNECTED = 'connected',
FAILED = 'failed'
}

export type TMediaProvider = {
loading: boolean;
connectionStatus: ConnectionStatus;
audioVideoRefsMap: Map<number, AudioVideoRefs>;
ownVoiceState: TVoiceUserState;
getOrCreateRefs: (remoteId: number) => AudioVideoRefs;
getConsumerCodec: (
remoteId: number,
kind: StreamKind
) => string | undefined;
isSimulcastConsumer: (remoteId: number, kind: StreamKind) => boolean;
getStreamQualityLayers: (
remoteId: number,
kind: StreamKind
) => TStreamQualityLayer[];
getStreamQuality: (remoteId: number, kind: StreamKind) => TStreamQuality;
setStreamQuality: (
remoteId: number,
kind: StreamKind,
quality: TStreamQuality
) => void;
pauseConsumer: (remoteId: number, kind: TRemoteUserStreamKinds) => void;
resumeConsumer: (remoteId: number, kind: TRemoteUserStreamKinds) => void;
init: (
routerRtpCapabilities: RtpCapabilities,
channelId: number
) => Promise<void>;
} & Pick<
ReturnType<typeof useLocalStreams>,
| 'localAudioStream'
| 'localVideoStream'
| 'localScreenShareStream'
| 'localScreenShareAudioStream'
> &
Pick<
ReturnType<typeof useRemoteStreams>,
'remoteUserStreams' | 'externalStreams'
> &
ReturnType<typeof useVoiceControls>;

const DEFAULT_TRANSPORT_STATS: TransportStatsData = {
producer: null,
consumer: null,
screenShare: null,
totalBytesReceived: 0,
totalBytesSent: 0,
isMonitoring: false,
currentBitrateReceived: 0,
currentBitrateSent: 0,
averageBitrateReceived: 0,
averageBitrateSent: 0
};

const MediaStatsContext = createContext<TransportStatsData>(
DEFAULT_TRANSPORT_STATS
);

const MediaProviderContext = createContext<TMediaProvider>({
loading: false,
connectionStatus: ConnectionStatus.DISCONNECTED,
audioVideoRefsMap: new Map(),
getOrCreateRefs: () => ({
videoRef: { current: null },
audioRef: { current: null },
screenShareRef: { current: null },
screenShareAudioRef: { current: null },
externalAudioRef: { current: null },
externalVideoRef: { current: null }
}),
getConsumerCodec: () => undefined,
isSimulcastConsumer: () => false,
getStreamQualityLayers: () => [],
getStreamQuality: () => ({ mode: 'auto' }),
setStreamQuality: () => {},
pauseConsumer: () => {},
resumeConsumer: () => {},
init: () => Promise.resolve(),
toggleMic: () => Promise.resolve(),
toggleSound: () => Promise.resolve(),
toggleWebcam: () => Promise.resolve(),
toggleScreenShare: () => Promise.resolve(),
ownVoiceState: {
micMuted: false,
soundMuted: false,
webcamEnabled: false,
sharingScreen: false
},
localAudioStream: undefined,
localVideoStream: undefined,
localScreenShareStream: undefined,
localScreenShareAudioStream: undefined,

remoteUserStreams: {},
externalStreams: {}
});

export { ConnectionStatus, MediaProviderContext, MediaStatsContext };
export type { AudioVideoRefs };
Loading
Loading