React Native - Twilio chat conversation does not receive messages via registered 'messageAdded' listener

976 views Asked by At

I am currently trying to migrate a mobile react-native application (working with expo) from twilio-chat to twilio-conversations. Already did it with the browser based agent-application which works fine.

The react-native app is able to send messages, which appear in the browser based app, but not in the message list of the mobile app itself. Also my "messageAdded" listener never gets called.

Here is the hook I wrote to handle the interaction with the conversation client (version 2.1.0):


// useTwilioConversation.tsx

import { Client, Conversation, Message } from '@twilio/conversations';
import { useEffect, useState } from 'react';

import { ConversationType, Session } from '../interfaces/Session';
import useConversationToken from './useConversationToken';
import { useSocket } from './useSocket';

interface ChatOptions {
  session: Session;
}

const useTwilioConversation = (options: ChatOptions) => {
  const [chatReady, setChatReady] = useState(false);
  const [chatConversation, setChatConversation] = useState<Conversation>();
  const [messages, setMessages] = useState<Message[]>([]);
  const [newMessageAvailable, setNewMessageAvailable] = useState<boolean>(
    false
  );

 useConversationToken({
    session: options.session,
    type: ConversationType.CHAT,
    callback: async (data) => {
      await createClient(data.token);
    },
  });

  useEffect(() => {
    return () => {
      if (chatConversation) {
        chatConversation.removeAllListeners();
        chatConversation.leave();
      }
    };
  }, []);

  // socket context for handling other application communication
  const socket = useSocket();

  const onMessageAdded = (message: Message) => {

    // never gets called
    console.log(
      '-------------------------------------------------------------->NEW MESSAGE: ',
      message
    );
    setMessages((prevMessages) => {
      return [...prevMessages, message];
    });
    setNewMessageAvailable(true);
  };

  const registerListeners = (chatConversation: Conversation) => {
    chatConversation.on('messageAdded', onMessageAdded);
 
    console.log(chatConversation.listenerCount('messageAdded')); // returns '2'
  };

  const onChatConnected = () => {
    socket.emit('deviceConnect', { sessionId: options.session.id });
  };

  const createClient = async (token: string) => {

    const client = await new Client(token, { logLevel: 'debug' });

// works fine
    client.on('stateChanged', (state) => {
      if (state === 'initialized') {
        onInit(client);
      }
    });
    client.on('messageAdded', onMessageAdded);
  };

  const onInit = async (client: Client) => {
    const conversation = await client.getConversationByUniqueName(
      options.session.room
    );

    if (conversation) {
     
      if (conversation.status !== 'joined') {
        client.on('conversationJoined', () => {
          registerListeners(conversation);
        });
        await conversation.join();
      } else {
        registerListeners(conversation);
      }
      if (options.session.description !== '')
        await conversation.sendMessage(options.session.description);

      const messages = await conversation.getMessages();
      setMessages(messages.items);
      onChatConnected();
      setChatConversation(conversation);
      setChatReady(true);
    }
  };

 

  const submitMessage = (message: string) => {
    chatConversation?.sendMessage(message);
  };

 
  const readMessages = () => {
    setNewMessageAvailable(false);
  };

  return {
    messages,
    submitMessage,
    chatReady,
    newMessageAvailable,
    readMessages,
  } as const;
};

export default useTwilioConversation;

EDIT: Added UseConversationToken.tsx for clarity

// useConversationToken.tsx

import { useEffect, useState } from 'react';

import { ConversationType, Session } from '../interfaces/Session';
import { useSocket } from './useSocket';

interface ConversationOptions {
  type: ConversationType;
  session: Session;
  callback?: (data: ConversationReadyData) => void;
  timeout?: number;
}

interface ConversationReadyData {
  sessionId: string;
  type: ConversationType;
  token: string;
}

const useConversationToken = (options: ConversationOptions) => {
  const [token, setToken] = useState<string | undefined>(undefined);
  const socket = useSocket();

  useEffect(() => {
    const message = `${options.type}ConversationReady`;
    if (socket) {
      socket.on(message, (data: ConversationReadyData) => {
        if (data.sessionId === options.session.id) {
          if (options.timeout && options.timeout > 0) {
            setTimeout(() => {
              setToken(data.token);
              if (options.callback) options.callback(data);
            }, options.timeout);
          } else {
            setToken(data.token);
            if (options.callback) options.callback(data);
          }
        }
      });
    }

    return () => {
      if (socket) socket.off(message);
    };
  }, [socket]);

  return token;
};

export default useConversationToken;


I also tried this code directly in place instead of using it as a hook which makes no difference.

After sending a few messages to the mobile client, I receive an error, that indicates that there was a problem sending log messages to the devlopment environment. "RangeError: Invalid string length"

Twilio debug logs can be found here: Logs

Updated useTwilioConversation hook code:

import { Client, Conversation, Message } from '@twilio/conversations';
import { useEffect, useState } from 'react';

import { ConversationType, Session } from '../interfaces/Session';
import useConversationToken from './useConversationToken';
import { useSocket } from './useSocket';

interface ChatOptions {
  session: Session;
}

const useTwilioConversation = (options: ChatOptions) => {
  const [client, setClient] = useState<Client>();
  const [chatReady, setChatReady] = useState(false);
  const [conversation, setConversation] = useState<Conversation>();
  const [messages, setMessages] = useState<Message[]>([]);
  const [newMessageAvailable, setNewMessageAvailable] = useState<boolean>(
    false
  );

  const token = useConversationToken({
    session: options.session,
    type: ConversationType.CHAT,
  });

  useEffect(() => {
    (async () => {
      if (token) {
        const client = new Client(token, { logLevel: 'debug' });
        setClient(client);

        client.on('stateChanged', async (state) => {
          if (state === 'initialized') {
            const conversation = await client.getConversationByUniqueName(
              options.session.room
            );
            setConversation(conversation);
          }
        });
      }
    })();

    return () => {
      if (client) {
        client.removeAllListeners();
        client.shutdown();
      }
    };
  }, [token]);

  useEffect(() => {
    (async () => {
      if (client && conversation) {
        const registerListeners = () => {
          conversation.on('messageAdded', onMessageAdded);


// DOES NOT GET CALLED TOO!
          conversation.on('participantLeft', () => {
            console.log('PARTICIPANT LEFT')
            );
          });
        };
        if (conversation.status !== 'joined') {
          client.on('conversationJoined', () => {
            registerListeners();
          });
          await conversation.join();
        } else {
          registerListeners();
        }
        if (options.session.description !== '')
          await conversation.sendMessage(options.session.description);

        const messages = await conversation.getMessages();
        setMessages(messages.items);
        onChatConnected();
        setChatReady(true);
      }
    })();

    return () => {
      if (conversation) {
        conversation.removeAllListeners();
        conversation.leave();
      }
    };
  }, [conversation]);

  // socket context for handling other application communication
  const socket = useSocket();

  const onMessageAdded = (message: Message) => {
    // never gets called
    console.log(
      '-------------------------------------------------------------->NEW MESSAGE: ',
      message
    );
    setMessages((prevMessages) => {
      return [...prevMessages, message];
    });
    setNewMessageAvailable(true);
  };

  const onChatConnected = () => {
    socket.emit('deviceConnect', { sessionId: options.session.id });
  };

  const submitMessage = (message: string) => {
    conversation?.sendMessage(message);
  };

  const readMessages = () => {
    setNewMessageAvailable(false);
  };

  return {
    messages,
    submitMessage,
    chatReady,
    newMessageAvailable,
    readMessages,
  } as const;
};

export default useTwilioConversation;


0

There are 0 answers