diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 4d9bd5df60..7c0af1ff2b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -3549,7 +3549,7 @@ class Account( if (isNew) { innerNote.event = innerEvent } - marmotGroupList.restoreMessage(groupId, innerNote) + marmotGroupList.addMessage(groupId, innerNote) } catch (e: Exception) { Log.w( "Account", diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt index 24d1dc92d6..2ffe74d75a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt @@ -100,11 +100,6 @@ fun MarmotGroupChatView( newMessageModel.init(accountViewModel) newMessageModel.load(nostrGroupId) - DisposableEffect(nostrGroupId) { - newMessageModel.chatroom?.markAsRead() - onDispose { } - } - // Resolve the navigation-supplied replyId (e.g. tapping reply on an MLS // message in the Notifications screen) into the actual Note once it has // landed in LocalCache. checkGetOrCreateNote is a no-op for unknown ids. diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt index dc91f269fc..7eff984dbf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt @@ -222,11 +222,16 @@ fun MarmotGroupListItem( onClick: () -> Unit, ) { val displayName by chatroom.displayName.collectAsStateWithLifecycle() - val unread by chatroom.unreadCount.collectAsStateWithLifecycle() val members by chatroom.members.collectAsStateWithLifecycle() val memberPubkeys = remember(members) { members.map { it.pubkey } } val newestMessage = chatroom.newestMessage + val lastReadTime by accountViewModel.account.loadLastReadFlow("MarmotGroup/$groupId").collectAsStateWithLifecycle() + val unread = + remember(newestMessage, lastReadTime) { + chatroom.messages.count { (it.createdAt() ?: Long.MIN_VALUE) > lastReadTime } + } + Row( modifier = Modifier diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt index 16e0941d2f..e09ef9f793 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt @@ -260,7 +260,6 @@ private fun MarmotGroupRoomCompose( nav: INav, ) { val displayName by chatroom.displayName.collectAsStateWithLifecycle() - val unread by chatroom.unreadCount.collectAsStateWithLifecycle() val author = lastMessage.author val noteEvent = lastMessage.event @@ -274,13 +273,15 @@ private fun MarmotGroupRoomCompose( stringRes(R.string.marmot_group_no_messages_yet) } + val lastReadTime by accountViewModel.account.loadLastReadFlow("MarmotGroup/${chatroom.nostrGroupId}").collectAsStateWithLifecycle() + ChannelName( channelIdHex = chatroom.nostrGroupId, channelPicture = null, channelTitle = { modifier -> ChannelTitleWithLabelInfo(groupName, R.string.marmot_group, modifier) }, channelLastTime = lastMessage.createdAt(), channelLastContent = lastContent, - hasNewMessages = unread > 0, + hasNewMessages = (lastMessage.createdAt() ?: Long.MIN_VALUE) > lastReadTime, loadProfilePicture = accountViewModel.settings.showProfilePictures(), loadRobohash = accountViewModel.settings.isNotPerformanceMode(), autoPlayGif = diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt index 92b63c5b89..432f90fbd7 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt @@ -53,7 +53,6 @@ class MarmotGroupChatroom( var memberCount = MutableStateFlow(0) var members = MutableStateFlow>(emptyList()) var newestMessage: Note? = null - val unreadCount = MutableStateFlow(0) /** * Tracks the most recent createdAt (seconds) of a kind:445 group event @@ -125,30 +124,6 @@ class MarmotGroupChatroom( } fun addMessageSync(msg: Note): Boolean = - syncLock.withLock { - if (msg !in messages) { - messages = messages + msg - msg.addGatherer(this) - - val createdAt = msg.createdAt() ?: 0L - if (createdAt > (newestMessage?.createdAt() ?: 0L)) { - newestMessage = msg - } - - unreadCount.value += 1 - changesFlow?.get()?.tryEmit(ListChange.Addition(msg)) - return@withLock true - } - return@withLock false - } - - /** - * Add a message that is being restored from persistent storage on app - * startup. Behaves like [addMessageSync] but does NOT bump the unread - * count — restored messages were already seen by the user in a previous - * session. - */ - fun restoreMessageSync(msg: Note): Boolean = syncLock.withLock { if (msg !in messages) { messages = messages + msg @@ -181,10 +156,6 @@ class MarmotGroupChatroom( return@withLock false } - fun markAsRead() { - unreadCount.value = 0 - } - fun recordRelayActivity( relay: NormalizedRelayUrl, createdAt: Long, @@ -220,7 +191,6 @@ class MarmotGroupChatroom( if (toRemove.isEmpty()) return@withLock toRemove messages = emptySet() newestMessage = null - unreadCount.value = 0 changesFlow?.get()?.tryEmit(ListChange.SetDeletion(toRemove)) toRemove } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt index 589a40a8e4..62921bd304 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt @@ -49,35 +49,7 @@ class MarmotGroupList( ) { if (!isDisplayableFeedMessage(msg)) return val chatroom = getOrCreateGroup(nostrGroupId) - val isSelfAuthored = msg.author?.pubkeyHex == ownerPubKey - // Use the quiet path for our own messages so the relay round-trip - // doesn't mark the user's own outgoing message as unread. - val added = - if (isSelfAuthored) { - chatroom.restoreMessageSync(msg) - } else { - chatroom.addMessageSync(msg) - } - if (added) { - noteToGroupIndex.getOrCreate(msg.idHex) { nostrGroupId } - if (isSelfAuthored) { - chatroom.ownerSentMessage = true - } - _groupListChanges.tryEmit(nostrGroupId) - } - } - - /** - * Add a message that was restored from persistent storage at app startup. - * Does not bump the chatroom's unread counter. - */ - fun restoreMessage( - nostrGroupId: HexKey, - msg: Note, - ) { - if (!isDisplayableFeedMessage(msg)) return - val chatroom = getOrCreateGroup(nostrGroupId) - if (chatroom.restoreMessageSync(msg)) { + if (chatroom.addMessageSync(msg)) { noteToGroupIndex.getOrCreate(msg.idHex) { nostrGroupId } if (msg.author?.pubkeyHex == ownerPubKey) { chatroom.ownerSentMessage = true