twilio / twilio-voice-react-native

Other
62 stars 22 forks source link

how to know if an incoming call is completed after accepting from the notification #336

Closed prototypeninja closed 3 months ago

prototypeninja commented 4 months ago

Good morning. I would like to know on Android how to know that an incoming call has ended after accepting it from the notification. The disconneted event only works for outgoing calls I use this version "@twilio/voice-react-native-sdk": "^1.0.0-rc8",

mhuynh5757 commented 4 months ago

Hi @prototypeninja, thanks for reaching out. For future issues, please make sure to fill out the issue template, it helps us out a lot with debugging issues and is not optional.

For your current issue, you can listen for the voice.on(Voice.Event.CallInviteAccepted, (call) => { call.on(Call.Event.Disconnected, ...) }) event. The internal Disconnected event should fire for outgoing and incoming calls.

mhuynh5757 commented 4 months ago

Please note that this API will actually be changing in the next release for better developer ergonomics.

prototypeninja commented 4 months ago

voice.on(Voice.Event.CallInviteAccepted,(call) => { console.log('call accept'); call.on(Call.Event.Disconnected,()=>{ console.log('call Disconnected'); }) }, ); TypeError: call.on is not a function (it is undefined), js engine: hermes

mhuynh5757 commented 3 months ago

Hi @prototypeninja I see, this is not an issue that we've seen before. Can you provide a more complete code snippet if possible? And can you please describe your environment in more detail, are you using a physical Android device or the emulator? What version of Android, etc.

prototypeninja commented 3 months ago

Good evening. Thank you for your reply. I am using a physical android 13 android phone. here is the conde of the context in full

`import React, { createContext, useContext, useState, useEffect } from 'react'; import { Voice,Call as TwilioCall, Call } from '@twilio/voice-react-native-sdk'; import { getUsers,getContactByNumber } from '../database/database'; import { useSelector,useDispatch } from 'react-redux'; import { updateAuth } from './reducers'; import { AppState } from 'react-native'; import { updatecalldata } from '../api/api'; import BackgroundService from 'react-native-background-actions'; import NetInfo from "@react-native-community/netinfo";

const sleep = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));

var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); const VoiceContext = createContext();

export const VoiceProvider = ({ children }) => { const isAuthenticated = useSelector((state) => state.data.isAuthenticated); const dispatch = useDispatch(); const [token, setToken] = useState(''); const [callinvite, setcallinvite] = useState(null); const [isLoad, setIsLoad] = useState(false); const [currentcontact, setcurrentcontact] = useState(false); const [callconnected, setcallconnected] = useState(false);

const [isRunning, setIsRunning] = useState(false);
const [elapsedTime, setElapsedTime] = useState(0);
const [callstatus, setcallstatus]=useState('')
const [currentcall, setcurrentcall]=useState(null)
const [currentsid, setcurrentsid]=useState(null)

const [currentnumber, setcurrentnumber] = useState(null);

const [ismute, setismute] = useState(false);
const [iswait, setiswait] = useState(false);

const [appState, setAppState] = useState(AppState.currentState);

const [appStateVisible, setAppStateVisible] = useState(appState.current);

const sendUpdateRequest=(id)=> {

return new Promise((resolve, reject) => {
  console.log("datauser.length00000000000000000")
  console.log(id)

    var raw = JSON.stringify({
      "jsonrpc": "2.0",
      "params": {
        id:id,
        "token": "token"
      }
    });

    console.log(raw)

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
    };

    NetInfo.fetch().then(state => {
      if (state.isConnected && state.isInternetReachable) {
        fetch("https://domaine/api/openit/updateusersate", requestOptions)
        .then(response => response.json())
        .then(result => resolve(result))
        .catch(error =>  reject(error) );
      } else {
        resolve("Pas de connexion Internet disponible.")
        // console.log("Pas de connexion Internet disponible.");
      }
    });

  })

}

const veryIntensiveTask = async (taskDataArguments) => { const { delay } = taskDataArguments; await new Promise(async (resolve) => { for (let i = 0; BackgroundService.isRunning(); i++) { console.log(i);

    // Effectuer la requête HTTP toutes les 15 secondes
   const usersate= await sendUpdateRequest(datauser[0].id);
   console.log(usersate)

    await sleep(delay);
  }
});

};

const options = { taskName: 'OpenitUpdate', taskTitle: 'User status update', taskDesc: "Checking the user's statue", taskIcon: { name: 'ic_launcher', type: 'mipmap', }, color: '#ff00ff', parameters: { delay: 15000, // 15 secondes }, };

useEffect(() => { const subscription = AppState.addEventListener('change', nextAppState => { console.log("appStateappStateappStateappStateappStateappStateappState") console.log(appState) if ( appState.match(/inactive|background/) && nextAppState === 'active' ) { console.log('App has come to the foreground!'); BackgroundService.stop(); } else if (appState.match(/active/) && nextAppState.match(/inactive|background/)) { // L'application est passée de l'état actif (en premier plan) à l'état background/inactive // Démarrez le service ici si nécessaire if(datauser.length>0){ BackgroundService.start(veryIntensiveTask, options);

    }

  }

  console.log('AppState', nextAppState);
  setAppState(nextAppState);
});

return () => {
  subscription.remove();
};

}, [appState]); // <-- Add appState to the dependency array

const updateactive=async ()=>{ if(datauser.length>0){ const usersate= await sendUpdateRequest(datauser[0].id); console.log(usersate) }

}

useEffect(() => { let interval; interval = setInterval(() => { updateactive() }, 15000);

return () => {
  clearInterval(interval);
};

}, []);

useEffect(() => {
  let interval;
  if (isRunning) {
    interval = setInterval(() => {
      setElapsedTime((prevTime) => prevTime + 1000); // Ajoute 1 seconde à chaque intervalle
    }, 1000);
  }

  return () => {
    clearInterval(interval);
  };
}, [isRunning]);

const startStop = () => {
  setIsRunning((prevState) => !prevState);
};

const reset = () => {
  setIsRunning(false);
  setElapsedTime(0);
};

const formatTime = (milliseconds) => {
  const seconds = Math.floor((milliseconds / 1000) % 60);
  const minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
  const hours = Math.floor(milliseconds / (1000 * 60 * 60));

  const formatNumber = (number) => (number < 10 ? `0${number}` : number);

  return `${formatNumber(hours)}:${formatNumber(minutes)}:${formatNumber(seconds)}`;
};

const datauser = getUsers();
const voice = new Voice();

const fetchToken = async (id) => {
  try {
    const userId = `mobile-${id}`;
    const response = await fetch(`https://domaine/voip/app/token/?id=${userId}`);

    if (!response.ok) {
      throw new Error('Network response was not ok');
    }

    const data = await response.json();
    console.log("data.token(data.token")
    console.log(data.token)

    await voice.register(data.token);

    console.log("6666666666666666666666")

    voice.on(Voice.Event.Registered, () => {
      console.log('Registrered');
    });

    voice.on('callInvite', (callInvite) => {
      console.log('callInvite');
    setcallinvite(callInvite);

    });

    voice.on(Voice.Event.CallInviteNotificationTapped, (call) => {
      console.log('notification touch');

    });

    voice.on(Voice.Event.CallInviteAccepted,(call) => {
      console.log('call accept');
      call.on(Call.Event.Disconnected,()=>{
        console.log('call Disconnected');
      }) 
    },
  );

    voice.on(Voice.Event.CallInviteRejected, () => {
      console.log('CallInviteRejected');
    });

    voice.on( Voice.Event.CancelledCallInvite,
      () => {
        console.log('CancelledCallInvite');
      }
    );

    setToken(data.token);
  } catch (error) {
    console.log('There was a problem with the fetch operation:', error);
  }
};

useEffect(() => {
  if(datauser.length!=0){
  dispatch(updateAuth({ key: 'token', value: token }));
  }
}, [token,datauser]);

useEffect(() => {
    console.log('ffffffffff')
    // console.log(token.trim())
    if(datauser.length!=0){
      if(token.trim()==''){
        console.log('eee')
        fetchToken(datauser[0].id).then(res=>{
            console.log('done')
        })
    }
    // BackgroundService.stop()
    }

}, [datauser]);

useEffect(() => {
    console.log(callinvite)
    // if(callinvite!=null){

    //     callinvite.on(TwilioCall.Event.ConnectFailure, async (error) =>{

    //         console.log('callinvite ConnectFailure')
    //     })

    //     callinvite.on(TwilioCall.Event.Reconnecting, (error) =>{
    //         console.log('callinvite reconnexion')
    //     } )

    //     callinvite.on(TwilioCall.Event.Disconnected, async (error) => {

    //         console.log('callinvite Disconnected')

    //     });

    //     callinvite.once(TwilioCall.Event.QualityWarningsChanged, (error) => {
    //         console.log('callinvite QualityWarningsChanged')
    //     });

    //     callinvite.once(TwilioCall.Event.Connected, async () => {
    //         setcallstatus('callinvite QualityWarningsChanged')

    //         })

    // }

}, [callinvite])

const makecall=async (number)=>{

    const contactFound = getContactByNumber(number);

    console.log(contactFound)
    if(contactFound.length!==0){
        setcurrentcontact(contactFound[0])
    }else{
        setcurrentcontact(false)
    }

    const call = await voice.connect(token,{
        params: {
          To: number.replace(/[^\d]/g, ''),
        },
      });

      setcurrentnumber(number)

    call.on(TwilioCall.Event.ConnectFailure, async (error) =>{
        console.log('ConnectFailure----:', error)
        console.log('ConnectFailure sid:', call.getSid());
        console.log(datauser[0].id)
        const dataupdate =await updatecalldata('domaine',call.getSid(),elapsedTime,datauser[0].id)
        setcallstatus('Disconnected')
        setcallconnected(false)
        setcurrentcall(null)
        setcurrentnumber(null)
        setcurrentsid(null)
        reset()

    }

    // navigation.goBack()
    );
    call.on(TwilioCall.Event.Reconnecting, (error) =>
        console.log('Reconnecting:', error),
    );
    call.on(TwilioCall.Event.Disconnected, async (error) => {
        // The type of error here is "TwilioError | undefined".
        console.log('Disconnected',currentsid)
        console.log('Disconnected sid:', call.getSid());
        console.log('elapsedTime sid:', elapsedTime);
        console.log(datauser[0].id)

        const dataupdate =await updatecalldata('domaine',call.getSid(),elapsedTime,datauser[0].id)

        console.log('55555555555555555',dataupdate)
        setcallstatus('Disconnected')
        setcallconnected(false)
        setcurrentcall(null);
        setcurrentnumber(null)
        setcurrentsid(null)
        reset()

    });

    call.on(TwilioCall.Event.Ringing, async (error) => {
        // The type of error here is "TwilioError | undefined".
        // navigation.goBack()
        setcallstatus('Ringing')
        setcallconnected(false)
        console.log('Ringing sid:', call.getSid());
        if(currentsid==null){
            setcurrentsid(call.getSid())
        }

        console.log(call.listeners())

    });

    call.once(TwilioCall.Event.QualityWarningsChanged, (error) => {
        console.log('Ringing:', error);
    });

    call.once(TwilioCall.Event.Connected, async () => {
        setcallstatus('Connected')
        setcallconnected(true)
        console.log('Connected sid:', call.getSid());
        console.log('Connected',currentsid)

       startStop()

        })

        setcurrentcall(call)

}

const rejectCall = async () => {
    if (!currentcall) {
      console.warn('No call invite to reject');
    } else {
      await currentcall.disconnect();

    }
}

const senddigit = async (digit) => {
    if (!currentcall) {
      console.warn('No call invite to reject');
    } else {
      await currentcall.sendDigits(digit);

    }
}

const togglemute = async () => {
    if (!currentcall) {
      console.warn('No call invite to reject');
    } else {
      await currentcall.mute(!ismute);
      setismute((prevState) => !prevState);

    }
}
const voiceContextValue = {
callinvite,
  makecall,
  isRunning,
  elapsedTime,
  startStop,
  reset,
  formatTime,
  currentcontact,
  callconnected,
  callstatus,
  currentcall,
  rejectCall,
  setcallstatus,
  currentnumber,
  senddigit,
  iswait,
  ismute,
  togglemute

};

return <VoiceContext.Provider value={voiceContextValue}>{children}</VoiceContext.Provider>;

}

export const useVoice = () => { const context = useContext(VoiceContext); if (!context) { throw new Error('useVoice must be used within a VoiceProvider'); } return context; };

`

mhuynh5757 commented 3 months ago

Hi @prototypeninja my apologies, the function signature of the Voice.Listener.CallInviteAccepted function should be (callInvite, call) => any, not what I previously mentioned. Your code should look like this instead

voice.on(Voice.Event.CallInviteAccepted, (callInvite, call) => {
  call.on(Call.Event.Disconnected, () => {
    ...
  });
});
mhuynh5757 commented 3 months ago

You can view the API docs for the version of the library that you're using by navigating to the tag in GitHub and going to docs/api/voice-react-native-sdk.md.

prototypeninja commented 3 months ago

thank you it worked