blog #1
|
@ -24,7 +24,7 @@ export const Agora = ({ sessionId, secret, stopCalling, remoteUser }: AgoraProps
|
||||||
|
|
||||||
useJoin(
|
useJoin(
|
||||||
{
|
{
|
||||||
appid: 'ed90c9dc42634e5687d4e2e0766b363f',
|
appid: process.env.NEXT_PUBLIC_AGORA_APPID,
|
||||||
channel: `${sessionId}-${secret}`,
|
channel: `${sessionId}-${secret}`,
|
||||||
token: null,
|
token: null,
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { ICameraVideoTrack, LocalVideoTrack, LocalVideoTrackProps, MaybePromiseOrNull } from 'agora-rtc-react';
|
||||||
|
import { useAwaited } from '../../../../utils/agora/tools';
|
||||||
|
|
||||||
|
interface CameraVideoTrackProps extends LocalVideoTrackProps {
|
||||||
|
/**
|
||||||
|
* A camera video track which can be created by `createCameraVideoTrack()`.
|
||||||
|
*/
|
||||||
|
readonly track?: MaybePromiseOrNull<ICameraVideoTrack>;
|
||||||
|
/**
|
||||||
|
* Device ID, which can be retrieved by calling `getDevices()`.
|
||||||
|
*/
|
||||||
|
readonly deviceId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CameraVideoTrack = ({
|
||||||
|
track: maybeTrack,
|
||||||
|
deviceId,
|
||||||
|
...props
|
||||||
|
}: CameraVideoTrackProps) => {
|
||||||
|
const track = useAwaited(maybeTrack);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (track && deviceId != null) {
|
||||||
|
track.setDevice(deviceId).catch(console.warn);
|
||||||
|
}
|
||||||
|
}, [deviceId, track]);
|
||||||
|
|
||||||
|
return <LocalVideoTrack track={maybeTrack} {...props} />;
|
||||||
|
};
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { HTMLProps, ReactNode } from 'react';
|
||||||
|
import { ICameraVideoTrack, IMicrophoneAudioTrack, MaybePromiseOrNull } from 'agora-rtc-react';
|
||||||
|
import { UserCover } from '../components';
|
||||||
|
import { MicrophoneAudioTrack } from './MicrophoneAudioTrack';
|
||||||
|
import { CameraVideoTrack } from './CameraVideoTrack';
|
||||||
|
|
||||||
|
interface LocalUserProps extends HTMLProps<HTMLDivElement> {
|
||||||
|
/**
|
||||||
|
* Whether to turn on the local user's microphone. Default false.
|
||||||
|
*/
|
||||||
|
readonly micOn?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether to turn on the local user's camera. Default false.
|
||||||
|
*/
|
||||||
|
readonly cameraOn?: boolean;
|
||||||
|
/**
|
||||||
|
* A microphone audio track which can be created by `createMicrophoneAudioTrack()`.
|
||||||
|
*/
|
||||||
|
readonly audioTrack?: MaybePromiseOrNull<IMicrophoneAudioTrack>;
|
||||||
|
/**
|
||||||
|
* A camera video track which can be created by `createCameraVideoTrack()`.
|
||||||
|
*/
|
||||||
|
readonly videoTrack?: MaybePromiseOrNull<ICameraVideoTrack>;
|
||||||
|
/**
|
||||||
|
* Whether to play the local user's audio track. Default follows `micOn`.
|
||||||
|
*/
|
||||||
|
readonly playAudio?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether to play the local user's video track. Default follows `cameraOn`.
|
||||||
|
*/
|
||||||
|
readonly playVideo?: boolean;
|
||||||
|
/**
|
||||||
|
* Device ID, which can be retrieved by calling `getDevices()`.
|
||||||
|
*/
|
||||||
|
readonly micDeviceId?: string;
|
||||||
|
/**
|
||||||
|
* Device ID, which can be retrieved by calling `getDevices()`.
|
||||||
|
*/
|
||||||
|
readonly cameraDeviceId?: string;
|
||||||
|
/**
|
||||||
|
* The volume. The value ranges from 0 (mute) to 1000 (maximum). A value of 100 is the current volume.
|
||||||
|
*/
|
||||||
|
readonly volume?: number;
|
||||||
|
/**
|
||||||
|
* Render cover image if playVideo is off.
|
||||||
|
*/
|
||||||
|
readonly cover?: string;
|
||||||
|
/**
|
||||||
|
* Children is rendered on top of the video canvas.
|
||||||
|
*/
|
||||||
|
readonly children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play/Stop local user camera and microphone track.
|
||||||
|
*/
|
||||||
|
export function LocalUser({
|
||||||
|
micOn,
|
||||||
|
cameraOn,
|
||||||
|
audioTrack,
|
||||||
|
videoTrack,
|
||||||
|
playAudio = false,
|
||||||
|
playVideo,
|
||||||
|
micDeviceId,
|
||||||
|
cameraDeviceId,
|
||||||
|
volume,
|
||||||
|
cover,
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
...props
|
||||||
|
}: LocalUserProps) {
|
||||||
|
playVideo = playVideo ?? !!cameraOn;
|
||||||
|
playAudio = playAudio ?? !!micOn;
|
||||||
|
return (
|
||||||
|
<div {...props} style={{
|
||||||
|
position: "relative",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
background: "#000",
|
||||||
|
...style
|
||||||
|
}}>
|
||||||
|
<CameraVideoTrack
|
||||||
|
deviceId={cameraDeviceId}
|
||||||
|
disabled={!cameraOn}
|
||||||
|
play={playVideo}
|
||||||
|
track={videoTrack}
|
||||||
|
/>
|
||||||
|
<MicrophoneAudioTrack
|
||||||
|
deviceId={micDeviceId}
|
||||||
|
disabled={!micOn}
|
||||||
|
play={playAudio}
|
||||||
|
track={audioTrack}
|
||||||
|
volume={volume}
|
||||||
|
/>
|
||||||
|
{cover && !cameraOn && <UserCover cover={cover} />}
|
||||||
|
<div style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
zIndex: 2,
|
||||||
|
}}>{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,14 +1,14 @@
|
||||||
import { LocalUser, useLocalMicrophoneTrack, useLocalCameraTrack, usePublish, useIsConnected } from 'agora-rtc-react';
|
import { useLocalMicrophoneTrack, useLocalCameraTrack, usePublish, useIsConnected } from 'agora-rtc-react';
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import { UserOutlined } from '@ant-design/icons';
|
import { UserOutlined } from '@ant-design/icons';
|
||||||
import { useLocalStorage } from '../../../../hooks/useLocalStorage';
|
import { useLocalStorage } from '../../../../hooks/useLocalStorage';
|
||||||
import { AUTH_USER } from '../../../../constants/common';
|
import { AUTH_USER } from '../../../../constants/common';
|
||||||
|
import { LocalUser } from './LocalUser';
|
||||||
|
|
||||||
type LocalUserPanelProps = {
|
type LocalUserPanelProps = {
|
||||||
calling: boolean;
|
calling: boolean;
|
||||||
micOn: boolean;
|
micOn: boolean;
|
||||||
cameraOn: boolean;
|
cameraOn: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const LocalUserPanel = ({
|
export const LocalUserPanel = ({
|
||||||
calling,
|
calling,
|
||||||
|
@ -18,26 +18,11 @@ export const LocalUserPanel = ({
|
||||||
const isConnected = useIsConnected();
|
const isConnected = useIsConnected();
|
||||||
const [userData] = useLocalStorage(AUTH_USER, '');
|
const [userData] = useLocalStorage(AUTH_USER, '');
|
||||||
const { faceImageUrl: userImage = '' } = userData ? JSON.parse(userData) : {};
|
const { faceImageUrl: userImage = '' } = userData ? JSON.parse(userData) : {};
|
||||||
|
|
||||||
const [playVideo, setPlayVideo] = useState(false);
|
|
||||||
const [playAudio, setPlayAudio] = useState(false);
|
|
||||||
|
|
||||||
const { localMicrophoneTrack } = useLocalMicrophoneTrack(micOn);
|
const { localMicrophoneTrack } = useLocalMicrophoneTrack(micOn);
|
||||||
const { localCameraTrack } = useLocalCameraTrack(cameraOn);
|
const { localCameraTrack } = useLocalCameraTrack(cameraOn);
|
||||||
|
|
||||||
usePublish([localMicrophoneTrack, localCameraTrack]);
|
usePublish([localMicrophoneTrack, localCameraTrack]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (calling) {
|
|
||||||
setPlayVideo(cameraOn)
|
|
||||||
}
|
|
||||||
}, [cameraOn]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (calling) {
|
|
||||||
setPlayAudio(micOn)
|
|
||||||
}
|
|
||||||
}, [micOn]);
|
|
||||||
|
|
||||||
return calling && isConnected ? (
|
return calling && isConnected ? (
|
||||||
<div className="b-agora__local_user">
|
<div className="b-agora__local_user">
|
||||||
{!cameraOn && (
|
{!cameraOn && (
|
||||||
|
@ -51,9 +36,6 @@ export const LocalUserPanel = ({
|
||||||
audioTrack={localMicrophoneTrack}
|
audioTrack={localMicrophoneTrack}
|
||||||
cameraOn={cameraOn}
|
cameraOn={cameraOn}
|
||||||
micOn={micOn}
|
micOn={micOn}
|
||||||
playAudio={playAudio}
|
|
||||||
playVideo={playVideo}
|
|
||||||
style={{ width: '100%', height: '100%' }}
|
|
||||||
videoTrack={localCameraTrack}
|
videoTrack={localCameraTrack}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { ReactNode, useEffect } from 'react';
|
||||||
|
import { IMicrophoneAudioTrack, LocalAudioTrack, LocalAudioTrackProps, MaybePromiseOrNull } from 'agora-rtc-react';
|
||||||
|
import { useAwaited } from '../../../../utils/agora/tools';
|
||||||
|
|
||||||
|
interface MicrophoneAudioTrackProps extends LocalAudioTrackProps {
|
||||||
|
/**
|
||||||
|
* A microphone audio track which can be created by `createMicrophoneAudioTrack()`.
|
||||||
|
*/
|
||||||
|
readonly track?: MaybePromiseOrNull<IMicrophoneAudioTrack>;
|
||||||
|
/**
|
||||||
|
* Device ID, which can be retrieved by calling `getDevices()`.
|
||||||
|
*/
|
||||||
|
readonly deviceId?: string;
|
||||||
|
|
||||||
|
readonly children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MicrophoneAudioTrack = ({
|
||||||
|
track: maybeTrack,
|
||||||
|
deviceId,
|
||||||
|
...props
|
||||||
|
}: MicrophoneAudioTrackProps) => {
|
||||||
|
const track = useAwaited(maybeTrack);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (track && deviceId != null) {
|
||||||
|
track.setDevice(deviceId).catch(console.warn);
|
||||||
|
}
|
||||||
|
}, [deviceId, track]);
|
||||||
|
|
||||||
|
return <LocalAudioTrack track={maybeTrack} {...props} />;
|
||||||
|
};
|
|
@ -40,10 +40,9 @@ export function RemoteVideoPlayer({
|
||||||
...props
|
...props
|
||||||
}: RemoteVideoPlayerProps) {
|
}: RemoteVideoPlayerProps) {
|
||||||
const resolvedClient = useRTCClient(client);
|
const resolvedClient = useRTCClient(client);
|
||||||
const hasVideo = resolvedClient.remoteUsers?.find(
|
const hasVideo = resolvedClient.remoteUsers?.find(user => user.uid === track?.getUserId())?.hasVideo;
|
||||||
user => user.uid === track?.getUserId(),
|
|
||||||
)?.hasVideo;
|
|
||||||
playVideo = playVideo ?? hasVideo;
|
playVideo = playVideo ?? hasVideo;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...props} style={{
|
<div {...props} style={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
@ -66,4 +65,4 @@ export function RemoteVideoPlayer({
|
||||||
}}>{children}</div>
|
}}>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -4,8 +4,6 @@ import AgoraRTC, { AgoraRTCProvider } from 'agora-rtc-react';
|
||||||
import { Session } from '../../../types/sessions';
|
import { Session } from '../../../types/sessions';
|
||||||
import { Agora } from './Agora';
|
import { Agora } from './Agora';
|
||||||
|
|
||||||
AgoraRTC.setLogLevel(0);
|
|
||||||
|
|
||||||
export const AgoraClient = ({ session, stopCalling, isCoach }: { session?: Session, stopCalling: () => void, isCoach: boolean }) => {
|
export const AgoraClient = ({ session, stopCalling, isCoach }: { session?: Session, stopCalling: () => void, isCoach: boolean }) => {
|
||||||
const remoteUser = isCoach ? (session?.clients?.length ? session?.clients[0] : undefined) : session?.coach;
|
const remoteUser = isCoach ? (session?.clients?.length ? session?.clients[0] : undefined) : session?.coach;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue