android: add realtime foreground local notifications with active chat gating
Some checks failed
CI / test (push) Failing after 2m12s
Some checks failed
CI / test (push) Failing after 2m12s
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package ru.daemonlord.messenger.core.notifications
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ActiveChatTracker @Inject constructor() {
|
||||
private val _activeChatId = MutableStateFlow<Long?>(null)
|
||||
val activeChatId: StateFlow<Long?> = _activeChatId.asStateFlow()
|
||||
|
||||
fun setActiveChat(chatId: Long) {
|
||||
_activeChatId.value = chatId
|
||||
}
|
||||
|
||||
fun clearActiveChat(chatId: Long) {
|
||||
if (_activeChatId.value == chatId) {
|
||||
_activeChatId.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,9 @@ interface ChatDao {
|
||||
)
|
||||
fun observeChatById(chatId: Long): Flow<ChatListLocalModel?>
|
||||
|
||||
@Query("SELECT display_title FROM chats WHERE id = :chatId LIMIT 1")
|
||||
suspend fun getChatDisplayTitle(chatId: Long): String?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertChats(chats: List<ChatEntity>)
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.daemonlord.messenger.data.chat.local.dao.ChatDao
|
||||
import ru.daemonlord.messenger.core.notifications.ActiveChatTracker
|
||||
import ru.daemonlord.messenger.core.notifications.ChatNotificationPayload
|
||||
import ru.daemonlord.messenger.core.notifications.NotificationDispatcher
|
||||
import ru.daemonlord.messenger.data.message.local.dao.MessageDao
|
||||
import ru.daemonlord.messenger.data.message.local.entity.MessageEntity
|
||||
import ru.daemonlord.messenger.domain.chat.repository.ChatRepository
|
||||
@@ -21,6 +24,8 @@ class HandleRealtimeEventsUseCase @Inject constructor(
|
||||
private val chatRepository: ChatRepository,
|
||||
private val chatDao: ChatDao,
|
||||
private val messageDao: MessageDao,
|
||||
private val notificationDispatcher: NotificationDispatcher,
|
||||
private val activeChatTracker: ActiveChatTracker,
|
||||
) {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
@@ -69,6 +74,27 @@ class HandleRealtimeEventsUseCase @Inject constructor(
|
||||
updatedSortAt = event.createdAt,
|
||||
)
|
||||
chatDao.incrementUnread(chatId = event.chatId)
|
||||
val activeChatId = activeChatTracker.activeChatId.value
|
||||
if (activeChatId != event.chatId) {
|
||||
val title = chatDao.getChatDisplayTitle(event.chatId) ?: "New message"
|
||||
val body = event.text?.takeIf { it.isNotBlank() }
|
||||
?: when (event.type?.lowercase()) {
|
||||
"image" -> "Photo"
|
||||
"video" -> "Video"
|
||||
"audio" -> "Audio"
|
||||
"voice" -> "Voice message"
|
||||
"file" -> "File"
|
||||
else -> "Open chat"
|
||||
}
|
||||
notificationDispatcher.showChatMessage(
|
||||
ChatNotificationPayload(
|
||||
chatId = event.chatId,
|
||||
messageId = event.messageId,
|
||||
title = title,
|
||||
body = body,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is RealtimeEvent.MessageUpdated -> {
|
||||
|
||||
@@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.daemonlord.messenger.core.notifications.ActiveChatTracker
|
||||
import ru.daemonlord.messenger.domain.chat.usecase.ObserveChatUseCase
|
||||
import ru.daemonlord.messenger.domain.chat.usecase.ObserveChatsUseCase
|
||||
import ru.daemonlord.messenger.domain.common.AppError
|
||||
@@ -53,6 +54,7 @@ class ChatViewModel @Inject constructor(
|
||||
private val observeChatUseCase: ObserveChatUseCase,
|
||||
private val observeChatsUseCase: ObserveChatsUseCase,
|
||||
private val handleRealtimeEventsUseCase: HandleRealtimeEventsUseCase,
|
||||
private val activeChatTracker: ActiveChatTracker,
|
||||
) : ViewModel() {
|
||||
|
||||
private val chatId: Long = checkNotNull(savedStateHandle["chatId"])
|
||||
@@ -62,6 +64,7 @@ class ChatViewModel @Inject constructor(
|
||||
private var lastReadMessageId: Long? = null
|
||||
|
||||
init {
|
||||
activeChatTracker.setActiveChat(chatId)
|
||||
handleRealtimeEventsUseCase.start()
|
||||
observeChatPermissions()
|
||||
observeMessages()
|
||||
@@ -587,4 +590,10 @@ class ChatViewModel @Inject constructor(
|
||||
is AppError.Unknown -> "Unknown error."
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
activeChatTracker.clearActiveChat(chatId)
|
||||
handleRealtimeEventsUseCase.stop()
|
||||
super.onCleared()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user