Django Channels and React Native giftedChat app problem receiving new messages

20 views Asked by At

I have react native app with chat in it. And backend with Django rest framework and django channels.

consumers.py

import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from .models import User, Connection, Message
import logging

from .serializers import (
    MessageSerializer
)
from app.serializers import (
    UserSerializer, 
)

logger = logging.getLogger(__name__)


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        user = self.scope['user']
        self.accept()
        if not user.is_authenticated:
            return

        self.username = user.username

        # Join user to group
        async_to_sync(self.channel_layer.group_add)(
            self.username, self.channel_name
        )



    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            self.username, self.channel_name
        )

    def receive(self, text_data):
        data = json.loads(text_data)
        data_source = data.get('source')

        self.send(text_data=json.dumps({"message": data.get('message')}))  
        

        if data_source == 'message.send':
            user = self.scope['user']
            connectionId = data.get('connectionId')
            message_text = data.get('message')
            receiver = User.objects.get(username=data.get('user_receiver'))
            try:
                connection = Connection.objects.get(id=connectionId)
            except Connection.DoesNotExist:
                connection = Connection.objects.create(
                    sender=user,
                    receiver=receiver,
                )
                return
            
            message = Message.objects.create(
                connection=connection,
                user=user,
                text=message_text
            )

            # Get recipient friend
            recipient = connection.sender
            if connection.sender == user:
                recipient = connection.receiver

            # Send new message back to sender
            serialized_message = MessageSerializer(
                message,
                context={
                    'user': user
                }
            )
            serialized_friend = UserSerializer(recipient)
            data = {
                'message': serialized_message.data,
                'friend': serialized_friend.data
            }
            self.send_group(user.username, 'message.send', data)

            # Send new message to receiver
            serialized_message = MessageSerializer(
                message,
                context={
                    'user': recipient
                }
            )
            serialized_friend = UserSerializer(user)
            data = {
                'message': serialized_message.data,
                'friend': serialized_friend.data
            }
            self.send_group(recipient.username, 'message.send', data)

        elif data_source == 'message.list':
            user = self.scope['user']
            connectionId = data.get('connectionId')
            try:
                connection = Connection.objects.get(id=connectionId)
            except Connection.DoesNotExist:
                print('Error: couldnt find connection')
                return

            messages = Message.objects.filter(
                connection=connection
            ).order_by('-created')

            serialized_messages = MessageSerializer(
                messages,
                context={'user': user},
                many=True
            )

            recipient = connection.sender
            if connection.sender == user:
                recipient = connection.receiver
            
            serialized_friend = UserSerializer(recipient)

            data = {
                'messages': serialized_messages.data if messages.exists() else [],
            }

            self.send_group(user.username, 'message.list', data)

    def send_group(self, group, source, data):
        response = {
            'type': 'broadcast_group',
            'source': source,
            'data': data
        }
        async_to_sync(self.channel_layer.group_send)(
            group, response
        )
    
    def broadcast_group(self, data):
        '''
        data:
            - type: 'broadcast_group'
            - source: where it originated from
            - data: what ever you want to send as a dict
        '''
        print('Broadcasting to group:', data)

        '''
        return data:
            - source: where it originated from
            - data: what ever you want to send as a dict
        '''
        self.send(text_data=json.dumps(data))

MessagesDmScreen.js:

import React, { useEffect, useState } from 'react';
import { GiftedChat } from 'react-native-gifted-chat';
import { View,Text, TextInput, TouchableOpacity, Platform } from 'react-native';
import { useSelector } from 'react-redux';
import uuid from 'react-native-uuid';

export const MessagesDmScreen = () => {
    const user = useSelector(state => state.auth);
    const [messages, setMessages] = useState([]);
    const [refresh, setRefresh] = useState('not refreshed');
    const [inputText, setInputText] = useState('');
    const socket = new WebSocket(`ws://127.0.0.1:8000/chat/?token=${user.token}`);

    useEffect(() => {
        socket.addEventListener('open', () => {
            // console.log(user.user.id,'WebSocket connection opened',new Date()); 
            socket.send(
                JSON.stringify({
                    source: 'message.list',
                    connectionId: 1,
                })
            );
        });
    }, []);

    socket.onmessage = (event) => {
        // Handle incoming messages from the server
        const data = JSON.parse(event.data);
        
        // Check the type of message
        if (data.source === 'message.list' && data.data && data.data.messages) {
            const receivedMessages = data.data.messages.map(msg => ({
                _id: msg._id,
                text: msg.text,
                createdAt: new Date(msg.created),
                user: {
                    _id: msg.user._id,
                    name: msg.user.username,
                    avatar: `http://127.0.0.1${msg.user.profile_image}`
                }
            }));
    
            // Filter out duplicate messages based on their ID
            const uniqueMessages = receivedMessages.filter(msg => !messages.find(existingMsg => existingMsg._id === msg._id));
            console.log(uniqueMessages);
    
            // Add unique messages to the state
            setMessages(prevMessages => [...prevMessages, ...uniqueMessages]);
        } else if (data.source === 'message.send' && data.data && data.data.message) {
            const sentMessage = data.data.message;
            // console.log(sentMessage);
    
            // Check if the message ID already exists in the state
            if (!messages.find(existingMsg => existingMsg._id === sentMessage._id)) {
                // Add the new message to the state
                const newMessage = {
                    _id: sentMessage._id,
                    text: sentMessage.text,
                    createdAt: new Date(sentMessage.created),
                    user: {
                        _id: sentMessage.user._id,
                        name: sentMessage.user.username,
                        avatar: `http://127.0.0.1${sentMessage.user.profile_image}`
                    }
                };
                setMessages(prevMessages => [...prevMessages, newMessage]);
            }
        } else {
            // Handle other types of messages or unexpected formats
            // console.log('Received unexpected message format:', data);
        }
    };

    const onSend = (newMessages) => {
        const messageText = newMessages[0].text;

        if (messageText.trim() === '') {
            return;
        }

        const newMessage = {
            _id: uuid.v4(),
            text: messageText,
            createdAt: new Date(),
            user: {
                _id: user.user.id,
                name: user.user.username,
                avatar: `http://127.0.0.1${user.user.profile_image}`
            },
        };

        setMessages((prevMessages) => GiftedChat.append(prevMessages, newMessage));

        socket.send(
            JSON.stringify({
                source: 'message.send',
                connectionId: 1,
                user_receiver:user.user.username == 'DiasOralbekov' ? 'Dias' : 'DiasOralbekov', 
                message: messageText,
            })
        );

    };

    return (
        <View style={{ flex: 1,marginBottom:100 }}>
            <GiftedChat
                messages={messages}
                onSend={onSend}
                isAnimated
                user={{
                    _id: user.user.id,
                }}
            />
        </View>
    );
};

Problem that when i send message Django Channels broadCasting it 4 times if i have 2 connected phone in chat websocket. But when I fetch messages like in socket.addEventListener('open') it works. And because of that i receive multiple messages and i cant set state messages right and i receive error on 2 connected to chat phones:

 ERROR  Warning: Encountered two children with the same key, `224`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

224 is id of message i sent.

Main goal is to make chat working, if you have other better ways to do chat functionality please let me know about it

0

There are 0 answers