fix: unreads work now for incoming messages, need to sync conv volatile properly still

This commit is contained in:
0x330a 2023-02-20 17:36:35 +11:00
parent 1b580cca1b
commit 8c512c7b6e
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
8 changed files with 58 additions and 33 deletions

View File

@ -479,7 +479,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
poller.setUserPublicKey(userPublicKey); poller.setUserPublicKey(userPublicKey);
return; return;
} }
poller = new Poller(configFactory); poller = new Poller(configFactory, new Timer());
} }
public void startPollingIfNeeded() { public void startPollingIfNeeded() {

View File

@ -128,8 +128,27 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
override fun markConversationAsRead(threadId: Long, lastSeenTime: Long) { override fun markConversationAsRead(threadId: Long, lastSeenTime: Long) {
val threadDb = DatabaseComponent.get(context).threadDatabase() val threadDb = DatabaseComponent.get(context).threadDatabase()
getRecipientForThread(threadId)?.let { getRecipientForThread(threadId)?.let { recipient ->
threadDb.markAllAsRead(threadId, it.isGroupRecipient, lastSeenTime) threadDb.markAllAsRead(threadId, recipient.isGroupRecipient, lastSeenTime)
configFactory.convoVolatile?.let { config ->
val convo = when {
// recipient closed group
recipient.isClosedGroupRecipient -> config.getOrConstructLegacyClosedGroup(recipient.address.serialize())
// recipient is open group
recipient.isOpenGroupRecipient -> {
val openGroupJoinUrl = getOpenGroup(threadId)?.joinURL ?: return
Conversation.OpenGroup.parseFullUrl(openGroupJoinUrl)?.let { (base, room, pubKey) ->
config.getOrConstructOpenGroup(base, room, pubKey)
} ?: return
}
// otherwise recipient is one to one
recipient.isContactRecipient -> config.getOrConstructOneToOne(recipient.address.serialize())
else -> throw NullPointerException("Weren't expecting to have a convo with address ${recipient.address.serialize()}")
}
convo.lastRead = lastSeenTime
config.set(convo)
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
}
} }
} }
@ -346,8 +365,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
} }
Log.d("Loki-DBG", "Should update thread $threadId") Log.d("Loki-DBG", "Should update thread $threadId")
if (threadId >= 0) { if (threadId >= 0) {
DatabaseComponent.get(context).threadDatabase() markConversationAsRead(threadId, conversation.lastRead)
.setLastSeen(threadId, conversation.lastRead) updateThread(threadId, false)
} }
} }
} }

View File

@ -64,8 +64,6 @@ import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.util.SessionMetaProtocol; import org.thoughtcrime.securesms.util.SessionMetaProtocol;
import java.io.Closeable; import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -101,8 +99,6 @@ public class ThreadDatabase extends Database {
public static final String HAS_SENT = "has_sent"; public static final String HAS_SENT = "has_sent";
public static final String IS_PINNED = "is_pinned"; public static final String IS_PINNED = "is_pinned";
public static final String LAST_SEEN_TRIGGER = "thread_last_seen_trigger";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " +
@ -522,9 +518,19 @@ public class ThreadDatabase extends Database {
contentValues.put(LAST_SEEN, lastSeenTime); contentValues.put(LAST_SEEN, lastSeenTime);
db.beginTransaction(); db.beginTransaction();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)}); db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)});
String countSubQuery = "SELECT COUNT(*) FROM "+SmsDatabase.TABLE_NAME+" AS s INNER JOIN "+TABLE_NAME+" AS t ON s.thread_id = t._id WHERE t._id = ? AND s."+SmsDatabase.DATE_SENT+" > t."+LAST_SEEN+""; String smsCountSubQuery = "SELECT COUNT(*) FROM "+SmsDatabase.TABLE_NAME+" AS s WHERE t."+ID+" = s."+SmsDatabase.THREAD_ID+" AND s."+SmsDatabase.DATE_SENT+" > t."+LAST_SEEN+" AND s."+SmsDatabase.READ+" = 0";
String reflectUpdates = "UPDATE "+TABLE_NAME+" AS t SET "+UNREAD_COUNT+" = ("+countSubQuery+") WHERE t."+ID+" = ?"; String smsMentionCountSubQuery = "SELECT COUNT(*) FROM "+SmsDatabase.TABLE_NAME+" AS s WHERE t."+ID+" = s."+SmsDatabase.THREAD_ID+" AND s."+SmsDatabase.DATE_SENT+" > t."+LAST_SEEN+" AND s."+SmsDatabase.READ+" = 0 AND s."+SmsDatabase.HAS_MENTION+" = 1";
db.query(reflectUpdates, new String[]{threadId+"", threadId+""}); String smsReactionCountSubQuery = "SELECT COUNT(*) FROM "+SmsDatabase.TABLE_NAME+" AS s WHERE t."+ID+" = s."+SmsDatabase.THREAD_ID+" AND s."+SmsDatabase.DATE_SENT+" > t."+LAST_SEEN+" AND s."+SmsDatabase.REACTIONS_UNREAD+" = 1";
String mmsCountSubQuery = "SELECT COUNT(*) FROM "+MmsDatabase.TABLE_NAME+" AS m WHERE t."+ID+" = m."+MmsDatabase.THREAD_ID+" AND m."+MmsDatabase.DATE_SENT+" > t."+LAST_SEEN+" AND m."+MmsDatabase.READ+" = 0";
String mmsMentionCountSubQuery = "SELECT COUNT(*) FROM "+MmsDatabase.TABLE_NAME+" AS m WHERE t."+ID+" = m."+MmsDatabase.THREAD_ID+" AND m."+MmsDatabase.DATE_SENT+" > t."+LAST_SEEN+" AND m."+MmsDatabase.READ+" = 0 AND m."+MmsDatabase.HAS_MENTION+" = 1";
String mmsReactionCountSubQuery = "SELECT COUNT(*) FROM "+MmsDatabase.TABLE_NAME+" AS m WHERE t."+ID+" = m."+MmsDatabase.THREAD_ID+" AND m."+MmsDatabase.DATE_SENT+" > t."+LAST_SEEN+" AND m."+MmsDatabase.REACTIONS_UNREAD+" = 1";
String allSmsUnread = "(("+smsCountSubQuery+") + ("+smsReactionCountSubQuery+"))";
String allMmsUnread = "(("+mmsCountSubQuery+") + ("+mmsReactionCountSubQuery+"))";
String allUnread = "(("+allSmsUnread+") + ("+allMmsUnread+"))";
String allUnreadMention = "(("+smsMentionCountSubQuery+") + ("+mmsMentionCountSubQuery+"))";
String reflectUpdates = "UPDATE "+TABLE_NAME+" AS t SET "+UNREAD_COUNT+" = "+allUnread+", "+UNREAD_MENTION_COUNT+" = "+allUnreadMention+" WHERE "+ID+" = ?";
db.execSQL(reflectUpdates, new Object[]{threadId});
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
notifyConversationListListeners(); notifyConversationListListeners();

View File

@ -88,10 +88,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV39 = 60; private static final int lokiV39 = 60;
private static final int lokiV40 = 61; private static final int lokiV40 = 61;
private static final int lokiV41 = 62; private static final int lokiV41 = 62;
private static final int lokiV42 = 63;
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
private static final int DATABASE_VERSION = lokiV42; private static final int DATABASE_VERSION = lokiV41;
private static final int MIN_DATABASE_VERSION = lokiV7; private static final int MIN_DATABASE_VERSION = lokiV7;
private static final String CIPHER3_DATABASE_NAME = "signal.db"; private static final String CIPHER3_DATABASE_NAME = "signal.db";
public static final String DATABASE_NAME = "signal_v4.db"; public static final String DATABASE_NAME = "signal_v4.db";
@ -596,10 +595,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(ConfigDatabase.CREATE_CONFIG_TABLE_COMMAND); db.execSQL(ConfigDatabase.CREATE_CONFIG_TABLE_COMMAND);
} }
if (oldVersion < lokiV42) {
// db.execSQL(ThreadDatabase.getCreateLastSeenTrigger());
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();

View File

@ -107,14 +107,14 @@ class ConfigFactory(private val context: Context,
} }
override fun persist(forConfigObject: ConfigBase) { override fun persist(forConfigObject: ConfigBase) {
listeners.forEach { listener ->
listener.notifyUpdates(forConfigObject)
}
when (forConfigObject) { when (forConfigObject) {
is UserProfile -> persistUserConfigDump() is UserProfile -> persistUserConfigDump()
is Contacts -> persistContactsConfigDump() is Contacts -> persistContactsConfigDump()
is ConversationVolatileConfig -> persistConvoVolatileConfigDump() is ConversationVolatileConfig -> persistConvoVolatileConfigDump()
} }
listeners.forEach { listener ->
listener.notifyUpdates(forConfigObject)
}
} }
override fun appendHash(configObject: ConfigBase, hash: String) { override fun appendHash(configObject: ConfigBase, hash: String) {

View File

@ -176,7 +176,7 @@ class BatchMessageReceiveJob(
} }
// increment unreads, notify, and update thread // increment unreads, notify, and update thread
// last seen will be the current last seen if not changed (re-computes the read counts for thread record) // last seen will be the current last seen if not changed (re-computes the read counts for thread record)
storage.markConversationAsRead(threadId, 0) storage.markConversationAsRead(threadId, myLastSeen)
storage.updateThread(threadId, true) storage.updateThread(threadId, true)
SSKEnvironment.shared.notificationManager.updateNotification(context, threadId) SSKEnvironment.shared.notificationManager.updateNotification(context, threadId)
} }

View File

@ -20,7 +20,7 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
override var failureCount: Int = 0 override var failureCount: Int = 0
override val maxFailureCount: Int = 1 override val maxFailureCount: Int = 1
override suspend fun execute() { override suspend fun execute(dispatcherName: String) {
val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair()
val userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey() val userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey()
val delegate = delegate val delegate = delegate
@ -37,7 +37,7 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
|| (destination is Destination.Contact && destination.publicKey != userPublicKey) || (destination is Destination.Contact && destination.publicKey != userPublicKey)
) { ) {
Log.w(TAG, "No need to run config sync job, TODO") Log.w(TAG, "No need to run config sync job, TODO")
delegate?.handleJobSucceeded(this) delegate?.handleJobSucceeded(this, dispatcherName)
return return
} }
@ -52,7 +52,7 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
).filter { config -> config.needsPush() } ).filter { config -> config.needsPush() }
// don't run anything if we don't need to push anything // don't run anything if we don't need to push anything
if (configsRequiringPush.isEmpty()) return delegate.handleJobSucceeded(this) if (configsRequiringPush.isEmpty()) return delegate.handleJobSucceeded(this, dispatcherName)
// allow null results here so the list index matches configsRequiringPush // allow null results here so the list index matches configsRequiringPush
val batchObjects: List<Pair<SharedConfigurationMessage, SnodeAPI.SnodeBatchRequestInfo>?> = configsRequiringPush.map { config -> val batchObjects: List<Pair<SharedConfigurationMessage, SnodeAPI.SnodeBatchRequestInfo>?> = configsRequiringPush.map { config ->
@ -79,7 +79,7 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
if (batchObjects.any { it == null }) { if (batchObjects.any { it == null }) {
// stop running here, something like a signing error occurred // stop running here, something like a signing error occurred
return delegate.handleJobFailedPermanently(this, NullPointerException("One or more requests had a null batch request info")) return delegate.handleJobFailedPermanently(this, dispatcherName, NullPointerException("One or more requests had a null batch request info"))
} }
val allRequests = mutableListOf<SnodeAPI.SnodeBatchRequestInfo>() val allRequests = mutableListOf<SnodeAPI.SnodeBatchRequestInfo>()
@ -145,9 +145,9 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error performing batch request", e) Log.e(TAG, "Error performing batch request", e)
return delegate.handleJobFailedPermanently(this, e) return delegate.handleJobFailedPermanently(this, dispatcherName, e)
} }
delegate.handleJobSucceeded(this) delegate.handleJobSucceeded(this, dispatcherName)
} }
fun Destination.destinationPublicKey(): String = when (this) { fun Destination.destinationPublicKey(): String = when (this) {

View File

@ -26,6 +26,7 @@ import org.session.libsession.snode.RawResponse
import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeAPI
import org.session.libsession.snode.SnodeModule import org.session.libsession.snode.SnodeModule
import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.ConfigFactoryProtocol
import org.session.libsession.utilities.WindowDebouncer
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Namespace import org.session.libsignal.utilities.Namespace
import org.session.libsignal.utilities.Snode import org.session.libsignal.utilities.Snode
@ -35,12 +36,14 @@ import java.util.TimerTask
private class PromiseCanceledException : Exception("Promise canceled.") private class PromiseCanceledException : Exception("Promise canceled.")
class Poller(private val configFactory: ConfigFactoryProtocol) { class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Timer) {
var userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey() ?: "" var userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey() ?: ""
private var hasStarted: Boolean = false private var hasStarted: Boolean = false
private val usedSnodes: MutableSet<Snode> = mutableSetOf() private val usedSnodes: MutableSet<Snode> = mutableSetOf()
var isCaughtUp = false var isCaughtUp = false
var configPollingJob: Job? = null var configPollingJob: Job? = null
val configDebouncer = WindowDebouncer(3000, debounceTimer)
// region Settings // region Settings
companion object { companion object {
@ -208,10 +211,12 @@ class Poller(private val configFactory: ConfigFactoryProtocol) {
if (key == Namespace.DEFAULT) { if (key == Namespace.DEFAULT) {
processPersonalMessages(snode, body) processPersonalMessages(snode, body)
} else { } else {
when (ConfigBase.kindFor(key)) { configDebouncer.publish {
UserProfile::class.java -> processConfig(snode, body, key, configFactory.user) when (ConfigBase.kindFor(key)) {
Contacts::class.java -> processConfig(snode, body, key, configFactory.contacts) UserProfile::class.java -> processConfig(snode, body, key, configFactory.user)
ConversationVolatileConfig::class.java -> processConfig(snode, body, key, configFactory.convoVolatile) Contacts::class.java -> processConfig(snode, body, key, configFactory.contacts)
ConversationVolatileConfig::class.java -> processConfig(snode, body, key, configFactory.convoVolatile)
}
} }
} }
} }