Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devusercode.data.firebase

import android.util.Log
import com.devusercode.core.domain.chat.model.UserPair
import com.devusercode.core.domain.chat.repo.ChatRepository
import com.devusercode.core.domain.user.model.User
Expand All @@ -9,13 +10,19 @@ import com.devusercode.data.local.mapper.toEntity
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.FirebaseDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext

class FirebaseChatRepository(
private val db: FirebaseDatabase,
private val conversationDao: ConversationDao,
) : ChatRepository {
companion object {
private const val TAG = "FirebaseChatRepository"
}

override suspend fun listOpenConversations(currentUid: String): List<UserPair> =
withContext(Dispatchers.IO) {
// 1) Try cache first
Expand All @@ -36,48 +43,63 @@ class FirebaseChatRepository(
return@withContext fresh
}

private suspend fun fetchFromNetwork(currentUid: String): List<UserPair> {
val convSnap =
db
.getReference("users")
.child(currentUid)
.child("conversations")
.get()
.await()
val cids = convSnap.children.mapNotNull { it.key }
val res = mutableListOf<UserPair>()

for (cid in cids) {
val parts =
db
.getReference("conversations")
.child(cid)
.child("participants")
.get()
.await()
val otherUid = parts.children.mapNotNull { it.key }.firstOrNull { it != currentUid } ?: continue
val otherSnap =
private suspend fun fetchFromNetwork(currentUid: String): List<UserPair> =
withContext(Dispatchers.IO) {
val convSnap =
db
.getReference("users")
.child(otherUid)
.get()
.await()
val user = otherSnap.toUser(otherUid)
val last =
db
.getReference("conversations")
.child(cid)
.child("lastMessage")
.child(currentUid)
.child("conversations")
.get()
.await()
val text = last.child("text").getValue(String::class.java)
val time = last.child("time").getValue(Long::class.java)
val cids = convSnap.children.mapNotNull { it.key }

res += UserPair(user, cid, text, time)
}
// Batch all Firebase calls concurrently instead of sequentially
cids.map { cid ->
async {
runCatching {
val parts =
db
.getReference("conversations")
.child(cid)
.child("participants")
.get()
.await()
val otherUid = parts.children.mapNotNull { it.key }.firstOrNull { it != currentUid } ?: return@async null

return res
}
// Fetch user data and last message in parallel
val otherSnapDeferred =
async {
db
.getReference("users")
.child(otherUid)
.get()
.await()
}
val lastDeferred =
async {
db
.getReference("conversations")
.child(cid)
.child("lastMessage")
.get()
.await()
}

val otherSnap = otherSnapDeferred.await()
val last = lastDeferred.await()

val user = otherSnap.toUser(otherUid)
val text = last.child("text").getValue(String::class.java)
val time = last.child("time").getValue(Long::class.java)

UserPair(user, cid, text, time)
}.onFailure { e ->
Log.e(TAG, "Failed to fetch conversation $cid", e)
}.getOrNull()
}
}.awaitAll().filterNotNull()
}

private fun DataSnapshot.toUser(uid: String): User {
val name =
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
13 changes: 10 additions & 3 deletions ui/src/main/java/com/devusercode/ui/screens/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,17 @@ fun HomeScreen(
title = { Text("UpChat") },
actions = {
IconButton(onClick = { menu = true }) { Icon(Icons.Default.MoreVert, contentDescription = null) }
DropdownMenu(expanded = menu, onDismissRequest = { }) {
DropdownMenuItem(text = { Text("Profile") }, onClick = { onProfile() })
DropdownMenuItem(text = { Text("Settings") }, onClick = { onSettings() })
DropdownMenu(expanded = menu, onDismissRequest = { menu = false }) {
DropdownMenuItem(text = { Text("Profile") }, onClick = {
menu = false
onProfile()
})
DropdownMenuItem(text = { Text("Settings") }, onClick = {
menu = false
onSettings()
})
DropdownMenuItem(text = { Text("Logout") }, onClick = {
menu = false
vm.doLogout {
// If NavHost passed a navigator, use it; else no-op
onLoggedOutNavigateToAuth?.invoke(Routes.AUTH)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,14 @@ class HomeViewModel
viewModelScope.launch {
observePresence(id).collect { (online, lastSeen) ->
_state.update { st ->
// Optimize: Only update the specific user at the given index
val updated =
st.conversations.map { p ->
if (p.user.uid == id) p.copy(user = p.user.copy(online = online, lastSeenEpochMs = lastSeen)) else p
st.conversations.toMutableList().apply {
val targetIndex = indexOfFirst { it.user.uid == id }
if (targetIndex >= 0) {
val pair = get(targetIndex)
set(targetIndex, pair.copy(user = pair.user.copy(online = online, lastSeenEpochMs = lastSeen)))
}
}
st.copy(conversations = updated)
}
Expand Down
Loading