Skip to content
Merged
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
Expand Up @@ -1796,6 +1796,18 @@ class ChatViewModel @AssistedInject constructor(
fun advanceLocalLastReadMessageIfNeeded(messageId: Int) {
Log.d(TAG, "advanceLocalLastReadMessageIfNeeded, messageId: $messageId")
Log.d(TAG, "advanceLocalLastReadMessageIfNeeded, localLastReadMessage: $localLastReadMessage")

val newestKnownRealMessageId = _uiState.value.conversation?.lastMessage?.id

if (!isPlausibleLastReadMessageId(messageId, newestKnownRealMessageId)) {
logger.w(
TAG,
"advanceLocalLastReadMessageIfNeeded, messageId ($messageId) is implausibly higher than the " +
"conversation's newest known message id ($newestKnownRealMessageId). We won't advance."
)
return
}

if (localLastReadMessage < messageId && -1 < messageId) {
Log.d(TAG, "advanceLocalLastReadMessageIfNeeded, setting localLastReadMessage to $messageId")
localLastReadMessage = messageId
Expand Down Expand Up @@ -2390,7 +2402,7 @@ class ChatViewModel @AssistedInject constructor(
}

companion object {
private val TAG = ChatViewModel::class.simpleName
private val TAG = ChatViewModel::class.java.simpleName
const val JOIN_ROOM_RETRY_COUNT: Long = 3
const val HTTP_CODE_OK: Int = 200
private const val CONVERSATION_AND_USER_FLOW_SHARING_TIMEOUT_MS = 5_000L
Expand All @@ -2406,6 +2418,11 @@ class ChatViewModel @AssistedInject constructor(
private const val LOAD_MORE_MESSAGES_LIMIT = 100
private const val POST_UPLOAD_FETCH_MAX_ATTEMPTS = 4
private const val POST_UPLOAD_FETCH_RETRY_DELAY_MS = 1_500L

private const val PLAUSIBLE_MESSAGE_ID_BUFFER = 10_000L

fun isPlausibleLastReadMessageId(messageId: Int, newestKnownRealMessageId: Long?): Boolean =
newestKnownRealMessageId == null || messageId <= newestKnownRealMessageId + PLAUSIBLE_MESSAGE_ID_BUFFER
}

sealed class OutOfOfficeUIState {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package com.nextcloud.talk.chat.viewmodels

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class ChatViewModelTest {

@Test
fun `isPlausibleLastReadMessageId returns true when there is no known real message id yet`() {
assertTrue(ChatViewModel.isPlausibleLastReadMessageId(messageId = 158761, newestKnownRealMessageId = null))
}

@Test
fun `isPlausibleLastReadMessageId returns true for the newest known real message id itself`() {
assertTrue(ChatViewModel.isPlausibleLastReadMessageId(messageId = 158761, newestKnownRealMessageId = 158761L))
}

@Test
fun `isPlausibleLastReadMessageId returns true for an older message id`() {
assertTrue(ChatViewModel.isPlausibleLastReadMessageId(messageId = 100, newestKnownRealMessageId = 158761L))
}

@Test
fun `isPlausibleLastReadMessageId returns true within the buffer above the newest known message id`() {
assertTrue(
ChatViewModel.isPlausibleLastReadMessageId(messageId = 158761 + 2000, newestKnownRealMessageId = 158761L)
)
}

@Test
fun `isPlausibleLastReadMessageId returns false just beyond the buffer above the newest known message id`() {
assertFalse(
ChatViewModel.isPlausibleLastReadMessageId(
messageId = 158761 + 2000 + 1,
newestKnownRealMessageId = 158761L
)
)
}

@Test
fun `isPlausibleLastReadMessageId rejects a hash-derived placeholder id far beyond the real message id space`() {
assertFalse(
ChatViewModel.isPlausibleLastReadMessageId(messageId = 1_963_726_147, newestKnownRealMessageId = 158761L)
)
}
}
Loading