twilio / twilio-voice-react-native

Other
74 stars 29 forks source link

Not able to receive calls in react-native app. #428

Open siddiquesheikh30 opened 1 month ago

siddiquesheikh30 commented 1 month ago

Issue

I need guidance on receiving incoming calls and making outgoing calls using Twilio and React Native. Could you provide more detailed steps or support on setting this up? for your info, I can provide you with my node Js backend code:

const express = require("express");
const twilio = require("twilio");
const cors = require("cors"); // Import cors
const bodyParser = require("body-parser");
const app = express();
const port = 3000; // You can change the port if necessary
const { VoiceResponse } = require("twilio").twiml;
// Twilio credentials
const accountSid = "Twilio Account SID"; // Twilio Account SID
const apiKey = "Twilio API Key SID"; // Twilio API Key SID
const apiSecret = "Twilio API Secret"; // Twilio API Secret
const outgoingApplicationSid = "TwiML App SID"; // TwiML App SID

const client = twilio(accountSid, "Auth token");
// Add CORS middleware
let corsOptions = {
  origin: true, // This allows all origins
};
app.use(cors(corsOptions));
app.use(
  bodyParser.urlencoded({
    extended: true,
  })
);
// parse application/json
app.use(bodyParser.json());
// Route to generate Twilio token
const identity = "client:username"; // Change this to a unique identity

app.get("/token", (req, res) => {
  // Identity could be unique per user; for testing, use 'client:username'

  // Create a new Voice Grant
  const VoiceGrant = twilio.jwt.AccessToken.VoiceGrant;
  const voiceGrant = new VoiceGrant({
    outgoingApplicationSid: outgoingApplicationSid,
    incomingAllow: true, // Allow incoming calls
  });

  // Create a new Access Token
  const AccessToken = twilio.jwt.AccessToken;
  const token = new AccessToken(accountSid, apiKey, apiSecret, { identity });
  token.addGrant(voiceGrant);

  // Serialize the token to a JWT
  const accessToken = token.toJwt();
  res.json({ token: accessToken });
});

app.post("/voice", (req, res) => {
  const { To, From } = req.body;
  console.log("to,from", req.body);
  const response = new VoiceResponse();

  const dial = response.dial({ callerId: From });
  dial.number(To);

  res.set("Content-Type", "text/xml");
  res.send(response.toString());
});

// Start the server
app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

And also I can give you my Front-end react-native code also:

import { useEffect, useState } from 'react';
import { SafeAreaView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { Voice } from '@twilio/voice-react-native-sdk';
import axios from 'axios';
import messaging from '@react-native-firebase/messaging';
import { Alert } from 'react-native';

function App() {
  const [screenName, setScreenName] = useState('');
  const [token, setToken] = useState('');
  const [voiceInstance, setVoiceInstance] = useState<any>(null);
  const [toNumber, setToNumber] = useState('+919405237197');
  const [fromNumber, setFromNumber] = useState('+447446527790');
  const [baseUrl, setBaseUrl] = useState(
    'https://d660-103-190-12-135.ngrok-free.app',
  );

  const handleToken = async () => {
    const response: any = await axios.get(baseUrl + '/token');
    console.log('response?.data?.token', response?.data?.token);
    setToken(response?.data?.token);
  };

  async function requestUserPermission() {
    const authStatus = await messaging().requestPermission();
    const enabled =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL;

    if (enabled) {
      console.log('Authorization status:', authStatus);
    }
  }

  useEffect(() => {
    requestUserPermission();
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });

    return unsubscribe;
  }, []);

  useEffect(() => {
    handleToken();
  }, []);
  // Handle incoming calls
  useEffect(() => {
    if (token) {
      const instance = new Voice();

      // Allow incoming calls
      instance.register(token);

      // Handle incoming calls
      instance.on(Voice.Event.CallInvite, (callInvite: any) => {
        console.log('Incoming call invite received:', callInvite);
        if (callInvite) {
          callInvite.accept(); // Accept the incoming call
          console.log('Call accepted');
        } else {
          console.error('No callInvite received');
        }
      });

      instance.on(Voice.Event.Registered, () => {
        console.log('Device registered successfully for incoming calls.');
      });
      instance.on(Voice.Event.Error, error => {
        console.error('Error during call event:', error);
      });

      setVoiceInstance(instance);

      // return () => {
      //   instance
      //     .unregister(token)
      //     .then(() => {
      //       console.log('Device unregistered successfully.');
      //     })
      //     .catch(error => {
      //       console.error('Error unregistering device:', error);
      //     });
      // };
    }
  }, [token]);

  return (
    <SafeAreaView>
      <View
        style={{
          display: 'flex',
          backgroundColor: 'black',
          height: '100%',
          padding: 10,
          alignItems: 'center',
          justifyContent: 'center',
        }}>
        <TextInput
          style={{
            height: 40,
            margin: 12,
            borderWidth: 1,
            padding: 10,
            backgroundColor: 'white',
            borderRadius: 10,
            width: 200,
            color: 'black'
          }}
          onChangeText={setToNumber}
          value={toNumber}
          placeholder="To"
          placeholderTextColor='black'
        />
        <TextInput
          style={{
            height: 40,
            margin: 12,
            borderWidth: 1,
            padding: 10,
            backgroundColor: 'white',
            borderRadius: 10,
            width: 200,
            color: 'black'
          }}
          onChangeText={setFromNumber}
          value={fromNumber}
          placeholder="From"
          placeholderTextColor='black'
        />
        <TouchableOpacity
          onPress={async () => {
            await voiceInstance.connect(token, {
              params: {
                To: toNumber,
                From: fromNumber,
              },
            }).then((res: any) => { console.log("Request Successfull" + JSON.stringify(res)) }).catch((err: any) => {
              console.log(err)
            })
          }}
          style={{
            backgroundColor: 'white',
            padding: 10,
            borderRadius: 10,
            width: 100,
            margin: 10,
          }}>
          <Text style={{ color: 'black', fontWeight: 700, textAlign: 'center' }}>
            Call
          </Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}
export default App;

The above code is my react-native code when I try to make an outbound call from my app using Twilio number to the verified number given in the code: the response I am getting on my server console is also given below:

to,from {
  ApplicationSid: '*********',
  ApiVersion: '2010-04-01',
  Called: '',
  Caller: 'client:client:username',
  CallStatus: 'ringing',
  From: '+447446527790',
  To: '+9194******97',
  CallSid: 'CA7a376ab036bae5e8ee9b66b2bc7a8aa6',
  Direction: 'inbound',
  AccountSid: '****'
}

As we can see here we are making an outbound call but getting Direction as 'inbound' but I can still call this number so can you please help me understand this? Also, what do I do to make an incoming call successful here and show it in the react-native app that I am receiving a call and can receive it? I am also adding call logs of my Twilio call logs so that you can see I can make calls here but please help make an app that can receive incoming calls also.

Software and Device Information

Please complete the following information.

mhuynh5757 commented 1 month ago

Hello @siddiquesheikh30 you have some personal info in your code blocks that you might want to redact, including your account SIDs and phone numbers. Also, please format your code blocks using triple backticks.

siddiquesheikh30 commented 1 month ago

Hi @mhuynh5757, I have updated it also I understood why it is saying Direction: 'inbound' it is because: caller---- parent call ----> Twilio ---- child call ----> Called okay, but I want to see incoming calls here what particular changes do I have to make in my node Js server and also in my react-native code?

mhuynh5757 commented 1 month ago

Hi @siddiquesheikh30 have you seen our reference app that utilizes this SDK? https://github.com/twilio/twilio-voice-react-native-app

It may provide some help and inspiration for getting incoming calls working in your application.

siddiquesheikh30 commented 1 month ago

Yes, hi @mhuynh5757 thank you, the incoming calls are working, but please tell me how to switch the mobile speaker on/off, hold/uphold the call, and mute/unmute the call. I have gone through https://github.com/twilio/twilio-voice-react-native-app this reference app but I am still unable to find the solution so if possible, please provide me the required methods to use.

mhuynh5757 commented 1 week ago

Hi @siddiquesheikh30 that repo has examples on how to mute/unmute a call, as well as hold/resume a call. Here are some relevant links.

For hold functionality: https://github.com/twilio/twilio-voice-react-native-app/blob/main/app/src/store/voice/call/activeCall.ts#L181

For mute functionality: https://github.com/twilio/twilio-voice-react-native-app/blob/main/app/src/store/voice/call/activeCall.ts#L145