mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 18:30:20 +01:00
Use db as SSOT for unread counter.
This commit is contained in:
committed by
Cody Henthorne
parent
a7a5f2e8c6
commit
e3e9f90094
@@ -986,7 +986,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||
list.addItemDecoration(lastSeenDecoration, 0);
|
||||
|
||||
if (lastSeen > 0) {
|
||||
lastSeenDisposable.add(conversationViewModel.getThreadUnreadCount()
|
||||
lastSeenDisposable.add(conversationViewModel.getThreadUnreadCount(lastSeen)
|
||||
.distinctUntilChanged()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(unreadCount -> {
|
||||
|
||||
@@ -9,14 +9,12 @@ import androidx.annotation.WorkerThread;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
|
||||
import org.thoughtcrime.securesms.database.Database;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewedUpdateJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
@@ -34,7 +32,6 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Scheduler;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
@@ -180,16 +177,14 @@ class ConversationRepository {
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
Observable<Optional<ThreadRecord>> getThreadRecord(long threadId) {
|
||||
if (threadId == -1L) {
|
||||
return Observable.just(Optional.empty());
|
||||
Observable<Integer> getUnreadCount(long threadId, long afterTime) {
|
||||
if (threadId <= -1L || afterTime <= 0L) {
|
||||
return Observable.just(0);
|
||||
}
|
||||
|
||||
return Observable.<Optional<ThreadRecord>> create(emitter -> {
|
||||
return Observable.<Integer> create(emitter -> {
|
||||
|
||||
DatabaseObserver.Observer listener = () -> {
|
||||
emitter.onNext(Optional.ofNullable(SignalDatabase.threads().getThreadRecord(threadId)));
|
||||
};
|
||||
DatabaseObserver.Observer listener = () -> emitter.onNext(SignalDatabase.mmsSms().getIncomingMeaningfulMessageCountSince(threadId, afterTime));
|
||||
|
||||
ApplicationDependencies.getDatabaseObserver().registerConversationObserver(threadId, listener);
|
||||
emitter.setCancellable(() -> ApplicationDependencies.getDatabaseObserver().unregisterObserver(listener));
|
||||
|
||||
@@ -95,9 +95,8 @@ public class ConversationViewModel extends ViewModel {
|
||||
private final GroupAuthorNameColorHelper groupAuthorNameColorHelper;
|
||||
private final RxStore<ConversationState> conversationStateStore;
|
||||
private final CompositeDisposable disposables;
|
||||
private final BehaviorSubject<Unit> conversationStateTick;
|
||||
private final RxStore<ThreadCountAggregator> threadCountStore;
|
||||
private final PublishProcessor<Long> markReadRequestPublisher;
|
||||
private final BehaviorSubject<Unit> conversationStateTick;
|
||||
private final PublishProcessor<Long> markReadRequestPublisher;
|
||||
|
||||
private ConversationIntents.Args args;
|
||||
private int jumpToPosition;
|
||||
@@ -126,7 +125,6 @@ public class ConversationViewModel extends ViewModel {
|
||||
this.conversationStateStore = new RxStore<>(ConversationState.create(), Schedulers.io());
|
||||
this.disposables = new CompositeDisposable();
|
||||
this.conversationStateTick = BehaviorSubject.createDefault(Unit.INSTANCE);
|
||||
this.threadCountStore = new RxStore<>(ThreadCountAggregator.Init.INSTANCE, Schedulers.computation());
|
||||
this.markReadRequestPublisher = PublishProcessor.create();
|
||||
|
||||
BehaviorSubject<Recipient> recipientCache = BehaviorSubject.create();
|
||||
@@ -137,11 +135,6 @@ public class ConversationViewModel extends ViewModel {
|
||||
.map(Recipient::resolved)
|
||||
.subscribe(recipientCache);
|
||||
|
||||
disposables.add(threadCountStore.update(
|
||||
threadId.switchMap(conversationRepository::getThreadRecord).toFlowable(BackpressureStrategy.BUFFER),
|
||||
(record, count) -> record.map(count::updateWith).orElse(count)
|
||||
));
|
||||
|
||||
conversationStateStore.update(Observable.combineLatest(recipientId, conversationStateTick, (id, tick) -> id)
|
||||
.distinctUntilChanged()
|
||||
.switchMap(conversationRepository::getSecurityInfo)
|
||||
@@ -307,13 +300,11 @@ public class ConversationViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
@NonNull Flowable<Long> getMarkReadRequests() {
|
||||
Flowable<ThreadCountAggregator> nonInitialThreadCount = threadCountStore.getStateFlowable().filter(count -> !(count instanceof ThreadCountAggregator.Init)).take(1);
|
||||
|
||||
return Flowable.combineLatest(markReadRequestPublisher.onBackpressureBuffer(), nonInitialThreadCount, (time, count) -> time);
|
||||
return markReadRequestPublisher.onBackpressureBuffer();
|
||||
}
|
||||
|
||||
@NonNull Flowable<Integer> getThreadUnreadCount() {
|
||||
return threadCountStore.getStateFlowable().map(ThreadCountAggregator::getCount);
|
||||
@NonNull Observable<Integer> getThreadUnreadCount(long afterTime) {
|
||||
return threadId.switchMap(id -> conversationRepository.getUnreadCount(id, afterTime));
|
||||
}
|
||||
|
||||
@NonNull Flowable<ConversationState> getConversationState() {
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package org.thoughtcrime.securesms.conversation
|
||||
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
|
||||
/**
|
||||
* Describes and aggregates the thread count for a particular thread, for use in the "Last Seen" header.
|
||||
*/
|
||||
sealed class ThreadCountAggregator {
|
||||
|
||||
abstract val count: Int
|
||||
|
||||
abstract fun updateWith(record: ThreadRecord): ThreadCountAggregator
|
||||
|
||||
/**
|
||||
* The Init object, used as an initial state and returned whenever the given record is an outgoing message.
|
||||
* The conversation fragment already properly cleans up the header when an outgoing message is emitted, so
|
||||
* there's no need to worry about seeing a "zero."
|
||||
*/
|
||||
object Init : ThreadCountAggregator() {
|
||||
override val count: Int = 0
|
||||
|
||||
override fun updateWith(record: ThreadRecord): ThreadCountAggregator {
|
||||
return when {
|
||||
record.isOutgoing -> Outgoing
|
||||
else -> Count(record.threadId, record.unreadCount, record.date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Outgoing object, returned whenever the given record is an outgoing message.
|
||||
* The conversation fragment already properly cleans up the header when an outgoing message is emitted, so
|
||||
* there's no need to worry about seeing a "zero."
|
||||
*/
|
||||
object Outgoing : ThreadCountAggregator() {
|
||||
override val count: Int = 0
|
||||
|
||||
override fun updateWith(record: ThreadRecord): ThreadCountAggregator {
|
||||
return when {
|
||||
record.isOutgoing -> Outgoing
|
||||
else -> Count(record.threadId, record.unreadCount, record.date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an actual count. We keep record of the id and date to use in comparisons with future
|
||||
* ThreadRecord objects.
|
||||
*/
|
||||
class Count(val threadId: Long, val unreadCount: Int, val threadDate: Long) : ThreadCountAggregator() {
|
||||
override val count: Int = unreadCount
|
||||
|
||||
/**
|
||||
* "Ratchets" the count to the new state.
|
||||
* * Outgoing records will always result in Empty
|
||||
* * Mismatched threadIds will always create a new Count, initialized with the new thread
|
||||
* * Matching dates will be ignored, as this means that there was no actual change.
|
||||
* * Otherwise, we'll proceed with the new date and aggregate the count.
|
||||
*/
|
||||
override fun updateWith(record: ThreadRecord): ThreadCountAggregator {
|
||||
return when {
|
||||
record.isOutgoing -> Outgoing
|
||||
threadId != record.threadId -> Init.updateWith(record)
|
||||
threadDate >= record.date -> this
|
||||
record.unreadCount > 1 -> Init.updateWith(record)
|
||||
else -> Count(threadId, unreadCount + 1, record.date)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user