mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-25 02:55:23 +00:00
fix: don't set the read flag in update notifications, some roundabout logic for first loads and scrolling to last known positions
This commit is contained in:
parent
ebdfd2538d
commit
cd66901412
@ -58,6 +58,7 @@ import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.consumeAsFlow
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import network.loki.messenger.R
|
||||
@ -174,6 +175,7 @@ import org.thoughtcrime.securesms.util.toPx
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.inject.Inject
|
||||
@ -272,7 +274,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
val searchViewModel: SearchViewModel by viewModels()
|
||||
var searchViewItem: MenuItem? = null
|
||||
|
||||
private val bufferedLastSeenChannel = Channel<Unit>(capacity = Channel.RENDEZVOUS, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
private val bufferedLastSeenChannel = Channel<Long>(capacity = 512, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
private val isScrolledToBottom: Boolean
|
||||
get() {
|
||||
@ -340,6 +342,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
private val cameraButton by lazy { InputBarButton(this, R.drawable.ic_baseline_photo_camera_24, hasOpaqueBackground = true) }
|
||||
private val messageToScrollTimestamp = AtomicLong(-1)
|
||||
private val messageToScrollAuthor = AtomicReference<Address?>(null)
|
||||
private val firstLoad = AtomicBoolean(true)
|
||||
|
||||
private lateinit var reactionDelegate: ConversationReactionDelegate
|
||||
private val reactWithAnyEmojiStartPage = -1
|
||||
@ -440,16 +443,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
// only update the conversation every 3 seconds maximum
|
||||
// channel is rendezvous and shouldn't block on try send calls as often as we want
|
||||
val layoutManager = binding?.conversationRecyclerView?.layoutManager as? LinearLayoutManager ?: return@repeatOnLifecycle
|
||||
val lastItemPos = layoutManager.findLastCompletelyVisibleItemPosition()
|
||||
// adapter.item
|
||||
withContext(Dispatchers.IO) {
|
||||
storage.markConversationAsRead(viewModel.threadId, SnodeAPI.nowWithOffset)
|
||||
}
|
||||
val bufferedFlow = bufferedLastSeenChannel.consumeAsFlow().debounce(3.seconds)
|
||||
bufferedFlow.collectLatest {
|
||||
val bufferedFlow = bufferedLastSeenChannel.consumeAsFlow()
|
||||
bufferedFlow.filter {
|
||||
it > storage.getLastSeen(viewModel.threadId)
|
||||
}.collectLatest { latestMessageRead ->
|
||||
withContext(Dispatchers.IO) {
|
||||
storage.markConversationAsRead(viewModel.threadId, SnodeAPI.nowWithOffset)
|
||||
storage.markConversationAsRead(viewModel.threadId, latestMessageRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -500,8 +499,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
val author = messageToScrollAuthor.getAndSet(null)
|
||||
if (author != null && messageTimestamp >= 0) {
|
||||
jumpToMessage(author, messageTimestamp, null)
|
||||
} else if (firstLoad.getAndSet(false)) {
|
||||
scrollToFirstUnreadMessageIfNeeded()
|
||||
}
|
||||
bufferedLastSeenChannel.trySend(Unit)
|
||||
}
|
||||
updatePlaceholder()
|
||||
}
|
||||
@ -990,7 +990,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
binding.typingIndicatorViewContainer.isVisible = wasTypingIndicatorVisibleBefore && isScrolledToBottom
|
||||
binding.typingIndicatorViewContainer.isVisible
|
||||
showOrHideScrollToBottomButton()
|
||||
val firstVisiblePosition = layoutManager?.findFirstVisibleItemPosition() ?: -1
|
||||
val firstVisiblePosition = layoutManager?.findFirstCompletelyVisibleItemPosition() ?: RecyclerView.NO_POSITION
|
||||
if (!firstLoad.get() && firstVisiblePosition != RecyclerView.NO_POSITION) {
|
||||
val visibleItemTimestamp = adapter.getTimestampForItemAt(firstVisiblePosition)
|
||||
if (visibleItemTimestamp != null) {
|
||||
bufferedLastSeenChannel.trySend(visibleItemTimestamp)
|
||||
}
|
||||
}
|
||||
unreadCount = min(unreadCount, firstVisiblePosition).coerceAtLeast(0)
|
||||
updateUnreadCountIndicator()
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
|
||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase.Reader
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
@ -219,11 +220,14 @@ class ConversationAdapter(
|
||||
|
||||
fun findLastSeenItemPosition(lastSeenTimestamp: Long): Int? {
|
||||
val cursor = this.cursor
|
||||
if (lastSeenTimestamp <= 0L || cursor == null || !isActiveCursor) return null
|
||||
if (cursor == null || !isActiveCursor) return null
|
||||
if (lastSeenTimestamp == 0L && cursor.moveToLast()) {
|
||||
return cursor.position
|
||||
}
|
||||
for (i in 0 until itemCount) {
|
||||
cursor.moveToPosition(i)
|
||||
val message = messageDB.readerFor(cursor).current
|
||||
if (message.isOutgoing || message.dateReceived <= lastSeenTimestamp) { return i }
|
||||
if (message.isOutgoing || message.dateSent <= lastSeenTimestamp) { return i }
|
||||
}
|
||||
return null
|
||||
}
|
||||
@ -243,4 +247,11 @@ class ConversationAdapter(
|
||||
this.searchQuery = query
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun getTimestampForItemAt(firstVisiblePosition: Int): Long? {
|
||||
val cursor = this.cursor ?: return null
|
||||
if (!cursor.moveToPosition(firstVisiblePosition)) return null
|
||||
val message = messageDB.readerFor(cursor).current ?: return null
|
||||
return message.timestamp
|
||||
}
|
||||
}
|
@ -64,6 +64,7 @@ import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.util.SessionMetaProtocol;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -313,8 +314,12 @@ public class ThreadDatabase extends Database {
|
||||
final List<MarkedMessageInfo> smsRecords = DatabaseComponent.get(context).smsDatabase().setMessagesRead(threadId, lastReadTime);
|
||||
final List<MarkedMessageInfo> mmsRecords = DatabaseComponent.get(context).mmsDatabase().setMessagesRead(threadId, lastReadTime);
|
||||
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(READ, 1);
|
||||
if (smsRecords.isEmpty() && mmsRecords.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
ContentValues contentValues = new ContentValues(2);
|
||||
contentValues.put(READ, smsRecords.isEmpty() && mmsRecords.isEmpty());
|
||||
contentValues.put(LAST_SEEN, lastReadTime);
|
||||
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
|
@ -240,10 +240,10 @@ public class DefaultMessageNotifier implements MessageNotifier {
|
||||
!(recipient.isApproved() || threads.getLastSeenAndHasSent(threadId).second())) {
|
||||
TextSecurePreferences.removeHasHiddenMessageRequests(context);
|
||||
}
|
||||
if (isVisible && recipient != null && threads.getMessageCount(threadId) > 0) {
|
||||
List<MarkedMessageInfo> messageIds = threads.setRead(threadId, true);
|
||||
if (SessionMetaProtocol.shouldSendReadReceipt(recipient)) { MarkReadReceiver.process(context, messageIds); }
|
||||
}
|
||||
// if (isVisible && recipient != null && threads.getMessageCount(threadId) > 0) {
|
||||
// List<MarkedMessageInfo> messageIds = threads.setRead(threadId, false);
|
||||
// if (SessionMetaProtocol.shouldSendReadReceipt(recipient)) { MarkReadReceiver.process(context, messageIds); }
|
||||
// }
|
||||
|
||||
if (!TextSecurePreferences.isNotificationsEnabled(context) ||
|
||||
(recipient != null && recipient.isMuted()))
|
||||
|
@ -70,9 +70,8 @@ fun MessageSender.create(name: String, members: Collection<String>): Promise<Str
|
||||
storage.addClosedGroupPublicKey(groupPublicKey)
|
||||
// Store the encryption key pair
|
||||
storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey, sentTime)
|
||||
// Notify the user
|
||||
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
|
||||
storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, threadID, sentTime)
|
||||
// Create the thread
|
||||
storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
|
||||
storage.createInitialConfigGroup(groupPublicKey, name, GroupUtil.createConfigMemberMap(members, admins), sentTime, encryptionKeyPair)
|
||||
// Notify the PN server
|
||||
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)
|
||||
|
@ -498,13 +498,8 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli
|
||||
storage.setExpirationTimer(groupID, expireTimer)
|
||||
// Notify the PN server
|
||||
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, storage.getUserPublicKey()!!)
|
||||
// Notify the user
|
||||
if (userPublicKey == sender && !groupExists) {
|
||||
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
|
||||
storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, threadID, sentTimestamp)
|
||||
} else if (userPublicKey != sender) {
|
||||
storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, sentTimestamp)
|
||||
}
|
||||
// Create thread
|
||||
storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
|
||||
// Start polling
|
||||
ClosedGroupPollerV2.shared.startPolling(groupPublicKey)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user