Sort chat-list by the most recent message with firebase

Viewed 1411

I don't know why I got stuck in a problem that the chatList is not sorting by the last message time or by the most recent message. I have tried storing timestamp in the database and orderChildBy timestamp but it still not working. not working means the list get not sort after every message and keep showing the list as the sorted after first message.

Look at the image how chats are disordered!

enter image description here

This is the way I created chatList in the firebaseDatabase in ChatActiviy on sendMessage:

    val timeAgo = Date().time

    val myTimeMap = HashMap<String, Any?>()
        myTimeMap["timestamp"] = timeAgo
        myTimeMap["id"] = friendId

    val friendTimeMap = HashMap<String, Any?>()
        friendTimeMap["timestamp"] = timeAgo
        friendTimeMap["id"] = currentUserID

    val chatListSenderReference = dbRef.child("ChatList").child(currentUserID).child(friendId)
        chatListSenderReference.keepSynced(true)
        chatListSenderReference.addListenerForSingleValueEvent(object : ValueEventListener{
              override fun onCancelled(p0: DatabaseError) {
              }
              override fun onDataChange(p0: DataSnapshot) {
                       if(!p0.exists()){
                             chatListSenderReference.updateChildren(friendTimeMap)
                       }
    val chatListReceiverReference = dbRef.child("ChatList").child(friendId).child(currentUserID)
        chatListReceiverReference.updateChildren(myTimeMap)
        }
    })

On retrieving the chatlist in recyclerView, I am trying to get the users details for each userswho is presented as the child of currentUser in database. (Chatlist>>CurrentUserId)

EDITED

  private fun retrieveChatList() {

    usersChatList = ArrayList()
    val userRef = dbRef.child("ChatList").child(currentUserID).orderByChild("timestamp")
    userRef.addValueEventListener(object : ValueEventListener
    {
        override fun onCancelled(error: DatabaseError) {
        }

        override fun onDataChange(snapshot: DataSnapshot)
        {
            (usersChatList as ArrayList<String>).clear()
            if (snapshot.exists()){
                for (dataSnapshot in snapshot.children){
                    val userUid = dataSnapshot.key
                    if (userUid != null) {
                        (usersChatList as ArrayList<String>).add(userUid)
                    }
                }
                readChatList()
            }
        }
    })
}

private fun readChatList() {
    mUsers = ArrayList()
    val userRef = FirebaseFirestore.getInstance().collection("Users")
    userRef.get()
            .addOnSuccessListener { queryDocumentSnapshots ->
                mUsers?.clear()
                for (documentSnapshot in queryDocumentSnapshots) {
                    val user = documentSnapshot.toObject(User::class.java)
                    for (id in usersChatList!!){
                        if (user.getUid() == id){
                            (mUsers as ArrayList<User>).add(user)
                        }
                    }
                }
                retrieveGroupChatList()
                chatListAdapter?.notifyDataSetChanged()
                chatListAdapter = context?.let { ChatListAdapter(it, (mUsers as ArrayList<User>), true) }
                recyclerViewChatList.adapter = chatListAdapter

            }.addOnFailureListener { e ->
                Log.d(ContentValues.TAG, "UserAdapter-retrieveUsers: ", e)
            }

}

And this is the chatListAdapter for friend info

private fun friendInfo(fullName: TextView, profileImage: CircleImageView, uid: String) {
        val userRef = FirebaseFirestore.getInstance().collection("Users").document(uid)
        userRef.get()
                .addOnSuccessListener {
                    if (it != null && it.exists()) {
                        val user = it.toObject(User::class.java)
                Picasso.get().load(user?.getImage()).placeholder(R.drawable.default_pro_pic).into(profileImage)
                fullName.text = user?.getFullName()
            }
        }
    }

This is the picture of the realtime database and has a model class as ChatList, every time when I send or receive a message timestamp gets an update.

ChatList

and another picture of Users in the firestore and has a model class as Users .

SOLUTION

I have a solution which works, Here i create or update a field as lastMessageTimestamp in the Firestore Users collection so the users now can sort by the lastMessageTimestamp .

   val timeAgo = Date().time
    
        val myFSMap = HashMap<String, Any?>()
            myFSMap["timestamp"] = timeAgo
    
        val friendFSMap = HashMap<String, Any?>()
            friendFSMap["timestamp"] = timeAgo
    
      //firebase chatlist references.
        val chatListSenderReference = dbRef.child("ChatList").child(currentUserID).child(friendId)
        val chatListReceiverReference = dbRef.child("ChatList").child(friendId).child(currentUserID)

      //Firestore Users references.
        val chatListSenderRef = fStore.collection("Users").document(currentUserID)
        val chatListReceiverRef = fStore.collection("Users").document(friendId)
    
        chatListSenderReference.addListenerForSingleValueEvent(object : ValueEventListener{
           override fun onDataChange(p0: DataSnapshot) {
                 if(!p0.exists()){
                    chatListSenderReference.setValue(friendId)
                    //update the timestamp in Users collection
                    chatListSenderRef.update(myFSMap)
                 }
                    chatListReceiverReference.setValue(currentUserID)
                    chatListReceiverRef.update(friendFSMap)

           override fun onCancelled(p0: DatabaseError) {
               }
            }
        })

And at the time of reading, I use orderBy for Users

 val userRef = FirebaseFirestore.getInstance().collection("Users").orderBy("lastMessageTimestamp" , Query.Direction.ASCENDING)

But It is not the complete solution because it seems like that i read and write the lastMessageTimestamp each time on messaging, which can Increase the Firebase Billing Amount to huge scary numbers. so i still need of a solution.

2 Answers

Simple trick is orderBy id of message. Because the id which generated by firebase base on realtime + a few factors. So let's try order by Id instead of ur timestamp. (note: just id which generated by firebase)

enter code hereSaw your post don't know if it might be useful this late hour, provided the only thing you want from firestone is the user full identity, like the name, picture etc use the userid and save the full details to android database then retrieve the identity using the Id from chatlist firebase database that matches userid

Your code might look like this

Read from chatlist firebase database Retrieve the sender Id and time Use the id to retrieve already added info of the person on android database your model should contain variable for retrieve time from database Then add all to list After that use a comparator to sort the arraylist/list base on time Then notify adapter change

{` ...... userDao = UserDatabase.getUserDatabase(requireContext()).userDao(); }

private void sortChatList() {

    reference.child("chatlist").child(firebaseUser.getUid()).orderByChild("time").addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

            list.clear();;
            for (DataSnapshot snapshot : dataSnapshot.getChildren()){
                String userID = Objects.requireNonNull(snapshot.child("chatid").getValue()).toString();
                 String time =  snapshot.child("time").getValue().toString();
                
                Chatlist chatlist = new Chatlist();


                UserDB userDB = userDao.getAll(userID);
                chatlist.setDate(time);
                chatlist.setUserName(userDB.getUserName());
                chatlist.setUserID(userID);
               


                list.add(chatlist);

            }
       
            Collections.sort(list, new Comparator<Chatlist>() {
                @Override
                public int compare(Chatlist o1, Chatlist o2) {
                    return Integer.valueOf(o2.getTime().compareTo(o1.getTime()));
                }
            });
            if (adapter != null) {

                adapter.notifyDataSetChanged();

            .........

`}

Related