How to launch a CometChat Direct Call outside of Chat

How to launch a CometChat Direct Call outside of Chat

Guide Details:

    "@cometchat/calls-sdk-javascript": "^4.0.9"

Introduction

In modern web applications, enabling users to initiate real-time communication, such as video calls, from various parts of the application can significantly enhance user interaction and engagement. In this tutorial, we’ll explore how to seamlessly integrate a CometChat Direct Call from any section of your application.

Note that this solution does not apply to default calling. Although the guide uses a Web React app and references the page URL, the solution is applicable to various devices using the CometChat SDK.

Solution Overview

By implementing the ability to initiate Direct Calls from various parts of your application, you can enrich user interactions and unlock new possibilities for real-time communication.

We need 2 things to launch the Direct call:

  1. The session id of the direct call to launch
  2. The logged in user’s uid

Before launching the call:

  1. The CometChatCalls Class needs to be initialized
  2. The user has to be logged into CometChat (SDK/UIKit)

Initialize CometChatCalls Class

Before attempting to launch a CometChat Call, you will need to ensure the class intialized correctly. You will need to add the following in your app. We recommend to do this right after intializing CometChat. You can review the set up documentation here.

const callAppSettings = new CometChatCalls.CallAppSettingsBuilder()
  .setAppId(appID)
  .setRegion(region)
  .build();

CometChatCalls.init(callAppSettings).then(
  () => {
    console.log("CometChatCalls initialization completed successfully");
  },
  (error) => {
    console.log("CometChatCalls initialization failed with error:", error);
  }
);

LogIn Component

Our LogIn Component performs the following tasks:

  • It retrieves the session ID from the URL parameters by querying for the ‘sessionId’ parameter
  • The user’s unique identifier (UID) is obtained via the login form, where it is entered as the ‘username’
  • The component creates and logs in the user into CometChat using the UID obtained from the form

Additionally, we utilize an effect hook within our component:

  • If both the ‘sessionId’ and the ‘ifError’ state are not set, the function sets the error state with a custom message.
  • If the ‘sessionId’ exists, it verifies whether the user is already logged into CometChat.
  • If authenticated, the component redirects the user to the ‘/call/${sessionId}’ page.

In your project implementation, you might already have the user ID, eliminating the need to prompt the end user for this information. Furthermore, the session ID retrieval can be adapted to suit your app’s specific structure or requirements.

function LoginForm() {
  const [username, setUsername] = useState("");

  const [ifError, setError] = useState();

  const navigate = useNavigate();

  const location = useLocation();
  const params = new URLSearchParams(location.search);
  const id = params.get("sessionId");

  useEffect(() => {
    if (!id && !ifError) {
      setError({
        message:
          "You won't be redirected to anywhere since there is not active call session",
      });
    }
    (async () => {
      if (id) {
        try {
          const user = await CometChat.getLoggedInUser();
          if (user) navigate(`/call/${id}`);
        } catch (e) {
          setError({ message: "" });
        }
      }
    })();
  }, []);

  const handleLogin = (e) => {
    e.preventDefault();

    if (!username) {
      setError({
        message: "please enter a username",
      });
    } else
      createUser(username.split(" ").concat().join("") + Date.now(), username)
        .then((user) => {
          CometChat.login(user.uid, AppConstants.authKey)
            .then(() => {
              setTimeout(() => {
                if (id) navigate(`/call/${id}`);
              }, 100);
            })
            .catch((err) => {
              console.log(err);
            });
        })
        .catch((error) => {
          console.log(error);
          setError(error);
        });
  };

  return (
    <div className='login-form'>
      <h1>Join Video Session</h1>
      <form onSubmit={handleLogin}>
        <label htmlFor='username'>Please Enter Your Name</label>
        <input
          type='text'
          id='username'
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
        <button type='submit'>Join</button>
      </form>
      {ifError ? (
        <div className='error-container'>
          <p>{ifError.message}</p>
        </div>
      ) : (
        <></>
      )}
    </div>
  );
}

CallWithAuth Component

Our route ‘/call/${sessionId}’ will launch the CallWithAuth component defined below.

const CallWithAuth = withAuth(Call);

withAuth Function

Being able to launch a CometChat Direct Call with an authentication check ensures that only authorized users can initiate or join calls, enhancing both security and user experience. The withAuth function acts as a higher-order component that adds authentication to any component it wraps. For our case, we are ensuring the user is logged into CometChat before attempting to launch the video meeting.

function withAuth(Component) {
  // generate the call auth token
  // this is where we would get an error if the user is not logged into cometchatr
  // use the auth token to start the call based on documentation

  return function WithAuthComponent(props) {
    const [isLoading, setIsLoading] = useState(true);
    const [isAuthenticated, setIsAuthenticated] = useState(0); // Initially not authorized
    const navigate = useNavigate();
    const { sessionId } = useParams();

    useEffect(() => {
      // Fetch user authentication data asynchronously

      if (isAuthenticated === 0) {
        CometChat.getLoggedInUser()
          .then((user) => {
            setIsLoading(false);

            setIsAuthenticated(user ? 1 : -1); // Example logic
          })
          .catch((error) => {
            setIsLoading(false);
            setIsAuthenticated(-1); // Example logic
          });
      }
    }, [isAuthenticated]);

    if (!isLoading && isAuthenticated === 1) {
      return <Component {...props} />;
    }

    if (isAuthenticated === -1) {
      navigate("/login?sessionId" + "=" + sessionId);
    }
  };
}

Once the user authentication status is successfully confirmed, the input component is returned.

Call Component

The Call component initiates the CometChat call once the user is authenticated. The following steps are done to launch the call:

  1. Retrieve sessionId from the parameters.
  2. Create a new CallSettingsBuilder.
    • Configure the CallSettingsBuilder.
  3. Generate a Call Token for the logged-in user.
  4. Use the CometChatCalls.startSession() function to launch the call.

At the end of the call, the isCallEnded state is set to true and a message is displayed to the user indicating the end of the session.

function Call(props) {
  const { sessionId } = useParams(); // Get parameter values
  const navigate = useNavigate();
  const [isCallEnded, setIsCallEnded] = useState(false);
  useEffect(() => {
    if (sessionId.trim()) {
      const cc = sessionId.trim();
      const callSettings = new CometChatCalls.CallSettingsBuilder()
        .enableDefaultLayout(true)
        .setIsAudioOnlyCall(false)
        .setCallListener(
          new CometChatCalls.OngoingCallListener({
            onUserListUpdated: (userList) => {
              console.log("user list:", userList);
            },
            onCallEnded: () => {
              console.log("call ended");
            },
            onError: (error) => {
              console.log("Error :", error);
            },
            onMediaDeviceListUpdated: (deviceList) => {
              console.log("Device List:", deviceList);
            },
            onUserMuted: (event) => {
              // This event will work in JS SDK v3.0.2-beta1 & later.
              console.log("Listener => onUserMuted:", {
                userMuted: event.muted,
                userMutedBy: event.mutedBy,
              });
            },
            onScreenShareStarted: () => {
              // This event will work in JS SDK v3.0.3 & later.
              console.log("Screen sharing started.");
            },
            onScreenShareStopped: () => {
              // This event will work in JS SDK v3.0.3 & later.
              console.log("Screen sharing stopped.");
            },
            onCallEndButtonPressed: () => {
              CometChatCalls.endSession();
              setIsCallEnded(true);
            },
            onCallSwitchedToVideo: (event) => {
              // This event will work in JS SDK v3.0.8 & later.
              console.log("call switched to video:", {
                sessionId: event.sessionId,
                callSwitchInitiatedBy: event.initiator,
                callSwitchAcceptedBy: event.responder,
              });
            },
            onUserJoined: (user) => console.log("event => onUserJoined", user),
            onUserLeft: (user) => console.log("event => onUserLeft", user),
          })
        )
        .build();

      CometChat.getLoggedInUser()
        .then((user) => {
          const authToken = user.getAuthToken();

          CometChatCalls.generateToken(cc, authToken).then(
            (res) => {
              CometChatCalls.startSession(
                res.token,
                callSettings,
                document.getElementById("calling")
              );
            },
            (err) => {
              console.log("Generating call token failed with error: ", err);
            }
          );
        })
        .catch((e) => {
          console.log(e);
        });
    } else {
      navigate("/login");
    }
  }, []);
  return (
    <>
      {isCallEnded ? (
        <div id='callEndMessage'>
          <p>Thank you! Your session has ended.</p>
          <br />
          <p> You can safely close the browser</p>
        </div>
      ) : (
        <></>
      )}
    </>
  );
}

See this app running here.