Hey @vic.yang
Can do, theres a decent amount of redudant code in here right now from testing stuff, but here it is.
import ZoomSdk, { MobileVideoFacingMode, Participant } from '@zoom/videosdk';
import { Stream } from '@zoom/videosdk/dist/types/media';
import { useConsultationService } from '~/common/services/consultation.service';
import { createTelehealthCallSession } from '~/common/api/TelehealthCallSession';
const CANVAS_WIDTH = 1280;
const CANVAS_HEIGHT = CANVAS_WIDTH * 9 / 16;
export enum TrackStatus {
Unknown = "Unknown",
Enabled = "Enabled",
Disabled = "Disabled",
On = "On",
Off = "Off"
}
interface UseZoomProps {
username: string;
isPatient: boolean;
consultationId: number;
}
interface UseZoomReturn {
canvasElementRef: React.RefObject<HTMLCanvasElement>;
selfViewElementRef: React.RefObject<HTMLVideoElement>;
trackStatus: TrackStatus;
otherTrackStatus: TrackStatus;
audioTrackStatus: TrackStatus;
toggleVideo: () => Promise<void>;
toggleCameraDirection: () => Promise<void>;
toggleAudio: () => Promise<void>;
leaveClient: () => Promise<void>;
joinClient: () => Promise<void>;
createNewSession: () => Promise<void>;
destroyClient: () => void;
}
export const useZoom = ({ username, isPatient, consultationId }: UseZoomProps): UseZoomReturn => {
const streamRef = useRef<typeof Stream | undefined>(undefined);
const [trackStatus, setTrackStatus] = useState<TrackStatus>(TrackStatus.Unknown);
const [otherTrackStatus, setOtherTrackStatus] = useState<TrackStatus>(TrackStatus.Unknown);
const [audioTrackStatus, setAudioTrackStatus] = useState<TrackStatus>(TrackStatus.Unknown);
const [videoTrackFacingDirection, setVideoTrackFacingDirection] = useState<MobileVideoFacingMode>(MobileVideoFacingMode.User);
const client = ZoomSdk.createClient();
const consultationService = useConsultationService();
const canvasElementRef = useRef<HTMLCanvasElement>(null);
const selfViewElementRef = useRef<HTMLVideoElement>(null);
const renderVideo = useCallback(async (elementRef: HTMLCanvasElement | HTMLVideoElement, userId: number) => {
if(streamRef.current) {
await streamRef.current.renderVideo(elementRef, userId, CANVAS_WIDTH, CANVAS_HEIGHT, undefined, undefined, 4);
}
}, []);
const initialiseCall = useCallback(async () => {
try {
const remoteViewElement = canvasElementRef.current;
const selfViewElement = selfViewElementRef.current;
const zoomJwtResponse = await consultationService.getZoomJwt(consultationId);
if (!zoomJwtResponse || !zoomJwtResponse.session || !zoomJwtResponse.jwt) {
console.error("Invalid Zoom JWT response", zoomJwtResponse);
return;
}
await client.init('en-US', 'Global', { patchJsMedia: true, leaveOnPageUnload: true });
await client.join(zoomJwtResponse.session, zoomJwtResponse.jwt, username);
console.log('session', client.getSessionInfo());
const zoomStream = await client.getMediaStream();
console.log(zoomStream)
streamRef.current = zoomStream;
client.getAllUser().forEach(async (user: Participant) => {
if (user.bVideoOn && remoteViewElement) {
await renderVideo(remoteViewElement, user.userId);
setOtherTrackStatus(TrackStatus.On);
}
});
client.on('peer-video-state-change', async (payload: { action: string; userId: any; }) => {
if (payload.action === 'Start' && remoteViewElement) {
setOtherTrackStatus(TrackStatus.On);
await renderVideo(remoteViewElement, payload.userId);
} else if (payload.action === 'Stop' && remoteViewElement) {
await zoomStream.stopRenderVideo(remoteViewElement, payload.userId);
setOtherTrackStatus(TrackStatus.Off);
}
});
client.on('user-removed', async () => {
await client.leave();
});
await zoomStream.startAudio();
setAudioTrackStatus(TrackStatus.On);
if (isPatient && selfViewElement) {
await zoomStream.startVideo({ hd: true });
await renderVideo(selfViewElement, client.getCurrentUserInfo().userId);
setTrackStatus(TrackStatus.On);
}
} catch (error) {
console.error("Error joining Zoom session:", error);
}
}, [client, consultationId, consultationService, isPatient, renderVideo, username]);
useEffect(() => {
void initialiseCall();
}, [initialiseCall]);
const toggleVideo = useCallback(async () => {
const selfViewElement = selfViewElementRef.current;
if (trackStatus === TrackStatus.Off || trackStatus === TrackStatus.Unknown) {
if (streamRef.current && selfViewElement) {
await streamRef.current.startVideo({ hd: true });
await renderVideo(selfViewElement, client.getCurrentUserInfo().userId);
setTrackStatus(TrackStatus.On);
}
} else {
if (streamRef.current && selfViewElement) {
setTrackStatus(TrackStatus.Off);
await streamRef.current.stopVideo();
await streamRef.current.stopRenderVideo(selfViewElement, client.getCurrentUserInfo().userId);
}
}
}, [client, renderVideo, trackStatus]);
const toggleCameraDirection = useCallback(async () => {
if (streamRef.current && trackStatus === TrackStatus.On) {
if (videoTrackFacingDirection === MobileVideoFacingMode.User) {
setVideoTrackFacingDirection(MobileVideoFacingMode.Environment);
await streamRef.current.switchCamera(MobileVideoFacingMode.Environment);
} else {
setVideoTrackFacingDirection(MobileVideoFacingMode.User);
await streamRef.current.switchCamera(MobileVideoFacingMode.User);
}
}
}, [trackStatus, videoTrackFacingDirection]);
const toggleAudio = useCallback(async () => {
if (audioTrackStatus === TrackStatus.Off) {
if (streamRef.current) {
await streamRef.current.unmuteAudio();
setAudioTrackStatus(TrackStatus.On);
}
} else {
if (streamRef.current) {
await streamRef.current.muteAudio();
setAudioTrackStatus(TrackStatus.Off);
}
}
}, [audioTrackStatus]);
const leaveClient = useCallback(async () => {
try {
console.log("Leaving Zoom session");
await client.leave();
// ZoomSdk.destroyClient();
} catch (error) {
console.error("Error leaving Zoom session:", error);
}
}, [client]);
const joinClient = useCallback(async () => {
try {
const remoteViewElement = canvasElementRef.current;
const zoomJwtResponse = await consultationService.getZoomJwt(consultationId);
console.log('joining zoom session with', zoomJwtResponse)
await client.init('en-US', 'Global', { patchJsMedia: true, leaveOnPageUnload: true });
await client.join(zoomJwtResponse.session, zoomJwtResponse.jwt, username);
console.log('session', client.getSessionInfo())
const zoomStream = await client.getMediaStream();
streamRef.current = zoomStream;
await streamRef.current.startVideo();
client.getAllUser().forEach(async (user: Participant) => {
if (user.bVideoOn && remoteViewElement && streamRef.current) {
await renderVideo(remoteViewElement, user.userId);
setOtherTrackStatus(TrackStatus.On);
}
});
} catch (error) {
console.error("Error joining Zoom session:", error);
}
}, [client, consultationId, consultationService, renderVideo, username]);
const createNewSession = useCallback(async () => {
await createTelehealthCallSession(consultationId);
}, [consultationId]);
const destroyClient = useCallback(() => {
ZoomSdk.destroyClient();
}, []);
return {
canvasElementRef,
selfViewElementRef,
trackStatus,
otherTrackStatus,
audioTrackStatus,
toggleVideo,
toggleCameraDirection,
toggleAudio,
leaveClient,
joinClient,
createNewSession,
destroyClient,
};
};