favoriabs / twilio-programmable-chat

MIT License
3 stars 4 forks source link

I got TypeError: Converting circular structure to JSON error in twilio joining to channel #12

Open sergeynilov opened 3 years ago

sergeynilov commented 3 years ago

Hello Reading docs https://www.twilio.com/blog/implementing-programmable-chat-php-laravel-vue-js and this /example I encounetered error in next: I try to add in my Laravel 8 / jQuery 3.5.1 / vue 2.6 / Bootstrap 4.5 chat when logged user select another user I run axios request to check if channel was created priorly(or create a new channel). On client part

            connectClientWithUsername(){
                this.tc.username = this.loggedUser.name

                let vm = this;
                axios.post('/admin/team-chat/check_channel', {
                    sender_id : vm.loggedUser.id,
                    receiver_id : vm.selectedTeamUser.id
                })
                    .then(({data}) => {
                        console.log('check_channel data::')
                        console.log(data)

                        vm.newChartCreated= data.newChartCreated // Flag if new chat was created
                        vm.currentChatChannelName= data.currentChatChannelName // Name of requested channel
                        vm.fetchAccessToken(vm.tc.username, vm.connectMessagingClient);

                    })
                    .catch(error => {
                        console.error(error)
                        popupAlert('Team Chat', error.response.data.message, 'warn')
                        vm.is_page_loaded = true
                    })
            },

            fetchAccessToken(username, handler) {
                let vm = this;
                axios.post('/token', {
                    identity: this.tc.username,
                    device: 'browser'
                })
                    .then(function (response) {
                        handler(response.data);
                    })
                    .catch(function (error) {
                        console.log(error);
                    });
            },

And on server part in app/Http/Controllers/Admin/TeamChatController.php:

    public function check_channel(Request $request)
    {
        $requestData = $request->all();
        $chatName= "public_oo_team_chat_" . $requestData['sender_id'] . '_' . $requestData['receiver_id'];
        $newChartCreated = false;
        $chatChannel = null;
        try {
            $channel = $this->twilio->chat->v2->services(config('app.TWILIO_SERVICE_SID'))
                                              ->channels($chatName)
                                              ->fetch();

        } catch(RestException $e) {
            $channel = $this->twilio->chat->v2->services(config('app.TWILIO_SERVICE_SID'))
                ->channels->create([
                    'uniqueName'    => $chatName,
                    'friendlyName'  => $chatName,
                    'type'          => 'public'  // New channel was created
                ]);
            if($channel->sid) {
                $chatChannel= new ChatChannel(); // Ref to newly created channel was saved in db
                $chatChannel->sender_id= $requestData['sender_id'];
                $chatChannel->receiver_id= $requestData['receiver_id'];
                $chatChannel->channel_name= $chatName;
                $chatChannel->last_chat_at= Carbon::now(config('app.timezone'));
                $chatChannel->save();
            }
            $newChartCreated= true;

        }
        return response()->json([
            'message' => '',
            'chatChannel' => $chatChannel,
            'newChartCreated' => $newChartCreated, // return name of current Channel
            'currentChatChannelName' => $chatName], HTTP_RESPONSE_OK);
    } // check_channel

    public function getToken(Request $request)
    {
        $this->identity = $request->identity;
        $token = new AccessToken(
            $this->twilio_account_sid,
            $this->twilio_api_key,
            $this->twilio_api_secret,
            3600,
            $this->identity
        );
        // Create Chat grant
        $chat_grant = new ChatGrant();
        $chat_grant->setServiceSid($this->service_sid);

        // Add grant to token
        $token->addGrant($chat_grant);
        // render token to string
        echo $token->toJWT();
    }

and I when I get token from server I create client and try to conect to channel

            connectMessagingClient(token) {  // connects the user to the Twilio Chat client.
                // Initialize the Chat messaging client
                let vm = this;

                this.tc.accessManager = new Twilio.AccessManager(token);

                new Twilio.Chat.Client.create(token).then(function(client) {
                    vm.tc.messagingClient = client;
                    vm.updateConnectedUI();
                    vm.connectToActiveChannel(client) // I try to connect to channel I need

                    // vm.tc.messagingClient.on('channelAdded', _.throttle(vm.loadChannelList));
                    // vm.tc.messagingClient.on('channelRemoved', _.throttle(vm.loadChannelList));
                    // vm.tc.messagingClient.on('tokenExpired', vm.refreshToken);
                });
            },

            connectToActiveChannel(messagingClient) {
                let vm = this

                // Get all public channels
                messagingClient.getPublicChannelDescriptors().then(function(channels) {
                    for (let i = 0; i < channels.items.length; i++) {
                        const channel = channels.items[i];
                    }
                    vm.tc.channelArray = channels.items;

                    vm.tc.channelArray.forEach(vm.addChannel); // Check for selected channel
                });

            },

            addChannel(channel){
                console.log('addChannel channel::')
                console.log(typeof channel)
                if (channel.uniqueName === this.currentChatChannelName) {
                    this.tc.generalChannel = channel;
                    console.log('FOUND  this.tc.generalChannel!!!')
                    console.log( this.tc.generalChannel )
                    return this.joinChannel(channel);
                }
            },

            joinChannel(_channel) { // the member joins the channel (general or a personally created channel)
                console.log(" joinChannel   _channel");
                console.log(_channel);
                let vm = this;
                return _channel.join()
                    .then(function(joinedChannel) {
                        console.log('Joined channel ' + joinedChannel.friendlyName);
                        vm.updateChannelUI(_channel);
                        vm.tc.currentChannel = _channel;
                        vm.loadMessages();
                        return joinedChannel;
                    })
                    .catch(function(err) {
                        alert("Couldn't join channel " + _channel.friendlyName + ' because ' + err);
                    });
            },

and in joinChannel I got error :

vue.common.dev.js?4650:630 [Vue warn]: Error in render: "TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'l'
    |     property '_fsm' -> object with constructor 'o'
    --- property 'context' closes the circle"

on line:

return _channel.join()

I see in the browser's console :

https://prnt.sc/wekctp and https://prnt.sc/wekspu

Looks like there is an error in my flow, but it seems to me that I passwed valid object to line : _channel.join

Why error and how it can be fixed?

Thanks!

sergeynilov commented 3 years ago

From the start the original flow seemed complicated to me and I tried to simplify it, but lost some functionality.

I returned to the original and made it working mostly So I read list of all channels with Twilio API anset current Channel in method:

                messagingClient.getPublicChannelDescriptors().then(function(channels) {
                    for (let i = 0; i < channels.items.length; i++) {
                        const channel = channels.items[i];
                        console.log('getVisibleChannels Channel: ' + channel.friendlyName);
                    }
                    // vm.tc.channelArray = vm.sortChannelsByName(channels.items);
                    vm.tc.channelArray = channels.items;

                    vm.tc.channelArray.forEach(vm.addChannel); // !!!
                    if (typeof handler === 'function') {
                        handler();
                    }
                });

but I got error : Couldn't join channel public_oo_team_chat_5_2 because t: Member already exists In the method:

            joinChannel(_channel) { // the member joins the channel (general or a personally created channel)
                // debugger
                let vm = this;
                return _channel.join()  // THis line raise the error
                    .then(function(joinedChannel) {
                        vm.updateChannelUI(_channel);
                        vm.tc.currentChannel = _channel;
                        vm.loadMessages();
                        return joinedChannel;
                    })
                    .catch(function(err) {
                        debugger
                    });
            },

In the twilio console I found the user in members of Chat / AdsBackend8Chat / Channels / Members

Can you please give me a hint which checks have I to use to avoid this error ?

Thanks!