Firebase를 사용하여 가장 최근 메시지를 기준으로 채팅 목록 정렬

Nov 23 2020

chatList 가 마지막 메시지 시간이나 가장 최근 메시지를 기준으로 정렬되지 않는 문제에 갇힌 이유를 모르겠습니다 . 나는 timestamp데이터베이스와 orderChildBy 타임 스탬프에 저장 을 시도 했지만 여전히 작동하지 않습니다. 작동하지 않음은 목록이 모든 메시지 다음에 정렬되지 않고 첫 번째 메시지 이후에 정렬 된 목록으로 계속 표시됨을 의미합니다.

채팅이 얼마나 무질서한지 이미지를보세요!

이것은 sendMessage의 ChatActiviyfirebaseDatabase 에서 chatList 를 만든 방법입니다 .

    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)
        }
    })

recyclerView에서 채팅 목록을 검색 할 때 데이터베이스에서 currentUser의 자식으로 표시되는 각 사용자에 대한 사용자 세부 정보를 가져 오려고합니다. (채팅 목록 >> CurrentUserId)

편집 됨

  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)
            }

}

그리고 이것은 친구 정보를위한 chatListAdapter입니다.

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()
            }
        }
    }

이것은 실시간 데이터베이스의 그림이며 메시지 타임 스탬프를 보내거나받을 때마다 업데이트를받을 때마다 ChatList 로 모델 클래스 가 있습니다.

그리고 또 다른 경우 FireStore에서 사용자의 사진과는 모델 클래스가 사용자를 .

해결책

작동하는 솔루션이 있습니다. 여기서 사용자 컬렉션 에서 lastMessageTimestamp 로 필드를 만들거나 업데이트 Firestore하여 사용자가 이제 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) {
               }
            }
        })

그리고 읽을 때 사용자를 위해 orderBy 를 사용합니다.

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

그러나 메시징에서 매번 lastMessageTimestamp를 읽고 쓰는 것처럼 보이기 때문에 완전한 솔루션은 아닙니다 Firebase Billing. 그래서 나는 여전히 해결책이 필요합니다.

답변

ChuongLeVan Nov 26 2020 at 08:21

간단한 트릭은 메시지의 orderBy id입니다. 실시간으로 firebase 기반으로 생성 한 id + 몇 가지 요인 때문입니다. 따라서 타임 스탬프 대신 Id로 주문 해 보겠습니다. (참고 : firebase에서 생성 한 id)