Ask
how to add image virtual background option in client view like on desktop app?
Stack
Nextjs
Screenshot
in down below (reply)
Code
"use client";
import config from "@/config/config";
import { buildQueryString } from "@/utils/string";
import { ZoomMtg } from "@zoom/meetingsdk";
import { useRouter, useSearchParams } from "next/navigation";
import { ChangeEvent, useEffect, useState } from "react";
import Button from "../Button";
import styles from "./style.module.css";
const { zoom } = config;
interface IMeetingData {
name: string;
mn: string;
passcode: string;
}
interface ZoomSignatureResponse {
signature: string;
}
const initializeZoomMeeting = (signature: string, data: IMeetingData) => {
ZoomMtg.i18n.load(zoom.lang as any);
ZoomMtg.init({
leaveUrl: `${zoom.leaveUrl}?${buildQueryString(data)}`,
disableRecord: true,
isSupportChat: false,
success: () => joinZoomMeeting(signature, data),
error: (error: any) => console.error("Zoom initialization error:", error),
});
};
const joinZoomMeeting = (signature: string, data: IMeetingData) => {
ZoomMtg.join({
sdkKey: zoom.clientId,
signature,
meetingNumber: data.mn,
passWord: data.passcode,
userName: data.name,
success: (success: any) =>
console.log("Successfully joined the meeting", success),
error: (error: any) => console.error("Error joining the meeting:", error),
});
};
const ZoomMeeting = () => {
const [isLoading, setIsLoading] = useState(false);
const [meetingData, setMeetingData] = useState<IMeetingData>({
name: "",
mn: "",
passcode: "",
});
const [showForm, setShowForm] = useState(true);
const searchParams = useSearchParams();
const { replace } = useRouter();
const getSignature = async (meetingNumber: string): Promise<string> => {
try {
const response = await fetch("/api/zoom", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ meetingNumber, role: 0 }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: ZoomSignatureResponse = await response.json();
if (!data.signature) {
throw new Error("Failed to get signature");
}
return data.signature;
} catch (error) {
console.error("Error getting signature:", error);
throw error;
}
};
const handleMeetingSetup = async (data: IMeetingData) => {
setIsLoading(true);
try {
replace(`?${buildQueryString(data)}`);
const signature = await getSignature(data.mn);
initializeZoomMeeting(signature, data);
} catch (error) {
console.error("Error setting up the meeting:", error);
} finally {
setIsLoading(false);
}
};
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setMeetingData((prev) => ({ ...prev, [name]: value }));
};
useEffect(() => {
const name = searchParams.get("name") || "";
const mn = searchParams.get("mn") || "";
const passcode = searchParams.get("passcode") || "";
setMeetingData({ name, mn, passcode });
if (name !== "" && mn !== "") {
setShowForm(false);
handleMeetingSetup({
name,
mn,
passcode,
});
} else {
setShowForm(true);
}
}, [searchParams]);
if (!showForm) {
return null;
}
return (
<main className={styles.container}>
<h1 className={styles.title}>Join Zoom Meeting</h1>
<form
onSubmit={(e) => {
e.preventDefault();
handleMeetingSetup(meetingData);
}}
className={styles.form}
>
{(["name", "mn", "passcode"] as const).map((field) => (
<div key={field} className={styles.formGroup}>
<label className={styles.label}>
{field === "mn"
? "Meeting Number"
: `${field.charAt(0).toUpperCase() + field.slice(1)}`}
</label>
<input
className={styles.input}
placeholder={`Enter ${field === "mn" ? "meeting number" : field}`}
value={meetingData[field]}
name={field}
onChange={handleInputChange}
required={field !== "passcode"}
/>
</div>
))}
<Button type="submit" disabled={isLoading}>
{isLoading ? "Loading..." : "Join Meeting"}
</Button>
</form>
</main>
);
};
export default ZoomMeeting;