support unread badge
This commit is contained in:
@@ -655,6 +655,8 @@ fun NewRequests(requests: List<Room>) {
|
|||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val totalUnread = requests.sumOf { it.unreadCount }
|
||||||
|
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
navigator.navigate(Screen.RequestList)
|
navigator.navigate(Screen.RequestList)
|
||||||
@@ -683,19 +685,31 @@ fun NewRequests(requests: List<Room>) {
|
|||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(
|
Text(
|
||||||
text = "Requests",
|
text = "Requests",
|
||||||
style = MaterialTheme.typography.titleMediumEmphasized
|
style = MaterialTheme.typography.titleMediumEmphasized.copy(
|
||||||
|
fontWeight = if (totalUnread > 0) FontWeight.SemiBold else FontWeight.Normal
|
||||||
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
if (supportingText.isNotEmpty()) {
|
if (supportingText.isNotEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
text = supportingText,
|
text = supportingText,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
fontWeight = if (totalUnread > 0) FontWeight.SemiBold else FontWeight.Normal,
|
||||||
|
color = if (totalUnread > 0) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.outline
|
||||||
|
),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
trailingContent = {
|
||||||
|
if (totalUnread > 0) {
|
||||||
|
androidx.compose.material3.Badge {
|
||||||
|
Text(totalUnread.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
colors = ListItemDefaults.colors(
|
colors = ListItemDefaults.colors(
|
||||||
containerColor = MaterialTheme.colorScheme.surface
|
containerColor = MaterialTheme.colorScheme.surface
|
||||||
)
|
)
|
||||||
@@ -721,13 +735,15 @@ fun ChatRoom(room: Room, onClick: () -> Unit) {
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = roomState.name,
|
text = roomState.name,
|
||||||
style = MaterialTheme.typography.titleMediumEmphasized,
|
style = MaterialTheme.typography.titleMediumEmphasized.copy(
|
||||||
|
fontWeight = if (room.unreadCount > 0) FontWeight.SemiBold else FontWeight.Normal
|
||||||
|
),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = room.createdAt.ago(),
|
text = room.createdAt.ago(),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.outline
|
color = if (room.unreadCount > 0) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -735,12 +751,22 @@ fun ChatRoom(room: Room, onClick: () -> Unit) {
|
|||||||
if (!room.lastMessage.isNullOrBlank()) {
|
if (!room.lastMessage.isNullOrBlank()) {
|
||||||
Text(
|
Text(
|
||||||
text = room.lastMessage ?: "",
|
text = room.lastMessage ?: "",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
fontWeight = if (room.unreadCount > 0) FontWeight.SemiBold else FontWeight.Normal,
|
||||||
|
color = if (room.unreadCount > 0) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.outline
|
||||||
|
),
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
trailingContent = {
|
||||||
|
if (room.unreadCount > 0) {
|
||||||
|
androidx.compose.material3.Badge {
|
||||||
|
Text(room.unreadCount.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
colors = ListItemDefaults.colors(
|
colors = ListItemDefaults.colors(
|
||||||
containerColor = MaterialTheme.colorScheme.surface
|
containerColor = MaterialTheme.colorScheme.surface
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ data class Room(
|
|||||||
val subject: String?,
|
val subject: String?,
|
||||||
val members: Set<PublicKey>,
|
val members: Set<PublicKey>,
|
||||||
val kind: RoomKind = RoomKind.default(),
|
val kind: RoomKind = RoomKind.default(),
|
||||||
val lastMessage: String? = null
|
val lastMessage: String? = null,
|
||||||
|
val unreadCount: Int = 0
|
||||||
) : Comparable<Room> {
|
) : Comparable<Room> {
|
||||||
override fun compareTo(other: Room): Int {
|
override fun compareTo(other: Room): Int {
|
||||||
return this.createdAt.asSecs().compareTo(other.createdAt.asSecs())
|
return this.createdAt.asSecs().compareTo(other.createdAt.asSecs())
|
||||||
|
|||||||
@@ -130,6 +130,19 @@ class ChatRepository(
|
|||||||
return _state.value.rooms[id]
|
return _state.value.rooms[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markAsRead(roomId: Long) {
|
||||||
|
_state.update { currentState ->
|
||||||
|
val rooms = currentState.rooms.toMutableMap()
|
||||||
|
val room = rooms[roomId]
|
||||||
|
if (room != null && room.unreadCount > 0) {
|
||||||
|
rooms[roomId] = room.copy(unreadCount = 0)
|
||||||
|
currentState.copy(rooms = rooms)
|
||||||
|
} else {
|
||||||
|
currentState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun refreshChatRooms() {
|
fun refreshChatRooms() {
|
||||||
scope.launch(defaultDispatcher) {
|
scope.launch(defaultDispatcher) {
|
||||||
try {
|
try {
|
||||||
@@ -140,10 +153,14 @@ class ChatRepository(
|
|||||||
val existing = newMap[dbRoom.id]
|
val existing = newMap[dbRoom.id]
|
||||||
// Only update if the database version is newer or equal
|
// Only update if the database version is newer or equal
|
||||||
if (existing == null || dbRoom.createdAt.asSecs() >= existing.createdAt.asSecs()) {
|
if (existing == null || dbRoom.createdAt.asSecs() >= existing.createdAt.asSecs()) {
|
||||||
// Preserve Ongoing kind if already marked as such in memory
|
// Preserve Ongoing kind and unreadCount status if already marked as such in memory
|
||||||
val mergedKind =
|
val mergedKind =
|
||||||
if (existing?.kind == RoomKind.Ongoing) RoomKind.Ongoing else dbRoom.kind
|
if (existing?.kind == RoomKind.Ongoing) RoomKind.Ongoing else dbRoom.kind
|
||||||
newMap[dbRoom.id] = dbRoom.copy(kind = mergedKind)
|
val mergedUnreadCount = existing?.unreadCount ?: 0
|
||||||
|
newMap[dbRoom.id] = dbRoom.copy(
|
||||||
|
kind = mergedKind,
|
||||||
|
unreadCount = mergedUnreadCount
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentState.copy(rooms = newMap)
|
currentState.copy(rooms = newMap)
|
||||||
@@ -237,14 +254,18 @@ class ChatRepository(
|
|||||||
|
|
||||||
if (existingRoom == null) {
|
if (existingRoom == null) {
|
||||||
// New room discovery
|
// New room discovery
|
||||||
val newRoom = Room.new(event, currentUser, roomId).copy(kind = newKind)
|
val newRoom = Room.new(event, currentUser, roomId).copy(
|
||||||
|
kind = newKind,
|
||||||
|
unreadCount = if (isFromMe) 0 else 1
|
||||||
|
)
|
||||||
rooms[newRoom.id] = newRoom
|
rooms[newRoom.id] = newRoom
|
||||||
} else if (event.createdAt().asSecs() >= existingRoom.createdAt.asSecs()) {
|
} else if (event.createdAt().asSecs() >= existingRoom.createdAt.asSecs()) {
|
||||||
// Only update preview if message is newer (handles sync/late arrivals)
|
// Only update preview if message is newer (handles sync/late arrivals)
|
||||||
rooms[roomId] = existingRoom.copy(
|
rooms[roomId] = existingRoom.copy(
|
||||||
lastMessage = event.content(),
|
lastMessage = event.content(),
|
||||||
createdAt = event.createdAt(),
|
createdAt = event.createdAt(),
|
||||||
kind = newKind
|
kind = newKind,
|
||||||
|
unreadCount = if (isFromMe) existingRoom.unreadCount else existingRoom.unreadCount + 1
|
||||||
)
|
)
|
||||||
} else if (isFromMe && existingRoom.kind != RoomKind.Ongoing) {
|
} else if (isFromMe && existingRoom.kind != RoomKind.Ongoing) {
|
||||||
// Even if it's an older message, if it's from me, the room is ongoing
|
// Even if it's an older message, if it's from me, the room is ongoing
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class ChatScreenViewModel(
|
|||||||
messages.clear()
|
messages.clear()
|
||||||
messages.addAll(initialMessages.distinctBy { it.id() })
|
messages.addAll(initialMessages.distinctBy { it.id() })
|
||||||
loading = false
|
loading = false
|
||||||
|
// Mark the room as read once messages are loaded
|
||||||
|
chatRepository.markAsRead(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +57,7 @@ class ChatScreenViewModel(
|
|||||||
if (event.roomId() == id) {
|
if (event.roomId() == id) {
|
||||||
if (messages.none { it.id() == event.id() }) {
|
if (messages.none { it.id() == event.id() }) {
|
||||||
messages.add(0, event)
|
messages.add(0, event)
|
||||||
|
chatRepository.markAsRead(id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newOtherMessages++
|
newOtherMessages++
|
||||||
|
|||||||
Reference in New Issue
Block a user