From 8fd09f92adebdd54ba1d1176d6b51dd29631e05f Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Sat, 14 Apr 2018 02:51:18 +0200
Subject: [PATCH] Allow actual usage of ignore rules

---
 .../quasseldroid/ui/chat/ChatActivity.kt      | 27 ++++++-
 .../quassel/syncables/BacklogManager.kt       | 21 ++---
 .../quassel/syncables/IgnoreListManager.kt    |  9 ++-
 .../quassel/syncables/RpcHandler.kt           |  2 +-
 .../libquassel/session/BacklogStorage.kt      |  8 +-
 .../persistence/QuasselBacklogStorage.kt      | 78 ++++++++++++-------
 .../persistence/QuasselDatabase.kt            | 38 +++++----
 7 files changed, 111 insertions(+), 72 deletions(-)

diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index 07a553b5c..67b2adf82 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -232,13 +232,20 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       }
     })
 
+    viewModel.connectionProgress
+      .filter { (it, _, _) -> it == ConnectionState.CONNECTED }
+      .firstElement()
+      .toLiveData()
+      .observe(this, Observer {
+        if (resources.getBoolean(R.bool.buffer_drawer_exists) && viewModel.buffer.value == -1) {
+          drawerLayout.openDrawer(Gravity.START)
+        }
+      })
+
     viewModel.connectionProgress.toLiveData().observe(this, Observer {
       val (state, progress, max) = it ?: Triple(ConnectionState.DISCONNECTED, 0, 0)
       when (state) {
         ConnectionState.CONNECTED -> {
-          if (resources.getBoolean(R.bool.buffer_drawer_exists) && viewModel.buffer.value == -1) {
-            drawerLayout.openDrawer(Gravity.START)
-          }
           progressBar.visibility = View.INVISIBLE
         }
         ConnectionState.DISCONNECTED,
@@ -413,6 +420,20 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     else                        -> super.onOptionsItemSelected(item)
   }
 
+  override fun onBackPressed() {
+    if (chatlineFragment?.historyPanel?.panelState == SlidingUpPanelLayout.PanelState.EXPANDED) {
+      chatlineFragment?.historyPanel?.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
+      return
+    }
+
+    if (editorPanel.panelState == SlidingUpPanelLayout.PanelState.EXPANDED) {
+      editorPanel.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
+      return
+    }
+
+    super.onBackPressed()
+  }
+
   private fun disconnect() {
     getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).editCommit {
       putBoolean(Keys.Status.reconnect, false)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt
index f40c8f2f8..a027bde17 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt
@@ -3,35 +3,26 @@ package de.kuschku.libquassel.quassel.syncables
 import de.kuschku.libquassel.protocol.*
 import de.kuschku.libquassel.quassel.syncables.interfaces.IBacklogManager
 import de.kuschku.libquassel.session.BacklogStorage
-import de.kuschku.libquassel.session.SignalProxy
-import java.util.concurrent.atomic.AtomicInteger
+import de.kuschku.libquassel.session.Session
 
 class BacklogManager(
-  proxy: SignalProxy,
+  private val session: Session,
   private val backlogStorage: BacklogStorage
-) : SyncableObject(proxy, "BacklogManager"), IBacklogManager {
+) : SyncableObject(session, "BacklogManager"), IBacklogManager {
   init {
     initialized = true
   }
 
-  private var loading = AtomicInteger(-1)
-
-  override fun requestBacklog(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int,
-                              additional: Int) {
-    if (loading.getAndSet(bufferId) != bufferId) {
-      super.requestBacklog(bufferId, first, last, limit, additional)
-    }
-  }
+  fun updateIgnoreRules() = backlogStorage.updateIgnoreRules(session)
 
   override fun receiveBacklog(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int,
                               additional: Int, messages: QVariantList) {
-    loading.compareAndSet(bufferId, -1)
-    backlogStorage.storeMessages(messages.mapNotNull(QVariant_::value), initialLoad = true)
+    backlogStorage.storeMessages(session, messages.mapNotNull(QVariant_::value), initialLoad = true)
   }
 
   override fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int,
                                  messages: QVariantList) {
-    backlogStorage.storeMessages(messages.mapNotNull(QVariant_::value), initialLoad = true)
+    backlogStorage.storeMessages(session, messages.mapNotNull(QVariant_::value), initialLoad = true)
   }
 
   fun removeBuffer(buffer: BufferId) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
index 5003a00ef..1f4b085a0 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
@@ -2,15 +2,15 @@ package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.*
 import de.kuschku.libquassel.quassel.syncables.interfaces.IIgnoreListManager
-import de.kuschku.libquassel.session.SignalProxy
+import de.kuschku.libquassel.session.Session
 import de.kuschku.libquassel.util.GlobTransformer
 import de.kuschku.libquassel.util.flag.and
 import io.reactivex.subjects.BehaviorSubject
 import java.io.Serializable
 
 class IgnoreListManager constructor(
-  proxy: SignalProxy
-) : SyncableObject(proxy, "IgnoreListManager"), IIgnoreListManager {
+  private val session: Session
+) : SyncableObject(session, "IgnoreListManager"), IIgnoreListManager {
   override fun toVariantMap(): QVariantMap = mapOf(
     "IgnoreList" to QVariant.of(initIgnoreList(), Type.QVariantMap)
   )
@@ -100,7 +100,7 @@ class IgnoreListManager constructor(
 
   fun updates() = live_updates.map { this }
 
-  fun copy() = IgnoreListManager(proxy).also {
+  fun copy() = IgnoreListManager(session).also {
     it.fromVariantMap(toVariantMap())
   }
 
@@ -265,5 +265,6 @@ class IgnoreListManager constructor(
     set(value) {
       field = value
       live_updates.onNext(Unit)
+      if (initialized) session.backlogManager.updateIgnoreRules()
     }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt
index a88c48fea..e02a481bf 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt
@@ -38,7 +38,7 @@ class RpcHandler(
 
   override fun displayMsg(message: Message) {
     session.bufferSyncer.bufferInfoUpdated(message.bufferInfo)
-    backlogStorage.storeMessages(message)
+    backlogStorage.storeMessages(session, message)
   }
 
   override fun requestCreateIdentity(identity: QVariantMap, additional: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt
index 6e4c080ec..3ce51bbae 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt
@@ -4,12 +4,14 @@ import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Message
 
 interface BacklogStorage {
-  fun storeMessages(vararg messages: Message, initialLoad: Boolean = false)
-  fun storeMessages(messages: Iterable<Message>, initialLoad: Boolean = false)
+  fun updateIgnoreRules(session: Session)
+
+  fun storeMessages(session: Session, vararg messages: Message, initialLoad: Boolean = false)
+  fun storeMessages(session: Session, messages: Iterable<Message>, initialLoad: Boolean = false)
 
   fun clearMessages(bufferId: BufferId, idRange: IntRange)
 
   fun clearMessages(bufferId: BufferId)
 
   fun clearMessages()
-}
\ No newline at end of file
+}
diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
index 37c141583..11a376822 100644
--- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
+++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
@@ -2,39 +2,39 @@ package de.kuschku.quasseldroid.persistence
 
 import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Message
+import de.kuschku.libquassel.protocol.Message_Type
+import de.kuschku.libquassel.quassel.syncables.IgnoreListManager
 import de.kuschku.libquassel.session.BacklogStorage
+import de.kuschku.libquassel.session.Session
 
 class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
-  override fun storeMessages(vararg messages: Message, initialLoad: Boolean) = storeMessages(
-    messages.asIterable(), initialLoad
-  )
-
-  override fun storeMessages(messages: Iterable<Message>, initialLoad: Boolean) {
-    if (initialLoad)
-      for ((bufferId, bufferMessages) in messages.sortedBy { it.messageId }.groupBy { it.bufferInfo.bufferId }) {
-        val lastMessageId = db.message().findLastByBufferId(bufferId)?.messageId
-        val firstMessage = bufferMessages.firstOrNull()
-        if (lastMessageId == null || firstMessage == null || lastMessageId < firstMessage.messageId) {
-          db.message().clearMessages(bufferId)
-        }
-      }
-
-    for (message in messages) {
-      db.message().save(
-        QuasselDatabase.DatabaseMessage(
-          messageId = message.messageId,
-          time = message.time,
-          type = message.type.value,
-          flag = message.flag.value,
-          bufferId = message.bufferInfo.bufferId,
-          sender = message.sender,
-          senderPrefixes = message.senderPrefixes,
-          realName = message.realName,
-          avatarUrl = message.avatarUrl,
-          content = message.content
-        )
+  override fun updateIgnoreRules(session: Session) {
+    db.message().save(
+      *db.message().all().map {
+        it.copy(ignored = isIgnored(session, it))
+      }.toTypedArray()
+    )
+  }
+
+  override fun storeMessages(session: Session, vararg messages: Message, initialLoad: Boolean) =
+    storeMessages(session, messages.asIterable(), initialLoad)
+
+  override fun storeMessages(session: Session, messages: Iterable<Message>, initialLoad: Boolean) {
+    db.message().save(*messages.map {
+      QuasselDatabase.DatabaseMessage(
+        messageId = it.messageId,
+        time = it.time,
+        type = it.type.value,
+        flag = it.flag.value,
+        bufferId = it.bufferInfo.bufferId,
+        sender = it.sender,
+        senderPrefixes = it.senderPrefixes,
+        realName = it.realName,
+        avatarUrl = it.avatarUrl,
+        content = it.content,
+        ignored = isIgnored(session, it)
       )
-    }
+    }.toTypedArray())
   }
 
   override fun clearMessages(bufferId: BufferId, idRange: IntRange) {
@@ -49,4 +49,24 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
     db.message().clearMessages()
   }
 
+  private fun isIgnored(session: Session, message: Message): Boolean {
+    val bufferName = message.bufferInfo.bufferName ?: ""
+    val networkId = message.bufferInfo.networkId
+    val networkName = session.network(networkId)?.networkName() ?: ""
+
+    return session.ignoreListManager.match(
+      message.content, message.sender, message.type, networkName, bufferName
+    ) != IgnoreListManager.StrictnessType.UnmatchedStrictness
+  }
+
+  private fun isIgnored(session: Session, message: QuasselDatabase.DatabaseMessage): Boolean {
+    val bufferInfo = session.bufferSyncer.bufferInfo(message.bufferId)
+    val bufferName = bufferInfo?.bufferName ?: ""
+    val networkId = bufferInfo?.networkId ?: -1
+    val networkName = session.network(networkId)?.networkName() ?: ""
+
+    return session.ignoreListManager.match(
+      message.content, message.sender, Message_Type.of(message.type), networkName, bufferName
+    ) != IgnoreListManager.StrictnessType.UnmatchedStrictness
+  }
 }
diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
index 672f16b1a..3730a8f67 100644
--- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
+++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
@@ -3,7 +3,6 @@ package de.kuschku.quasseldroid.persistence
 import android.arch.lifecycle.LiveData
 import android.arch.paging.DataSource
 import android.arch.persistence.db.SupportSQLiteDatabase
-import android.arch.persistence.db.SupportSQLiteQuery
 import android.arch.persistence.room.*
 import android.arch.persistence.room.migration.Migration
 import android.content.Context
@@ -16,13 +15,13 @@ import de.kuschku.quasseldroid.persistence.QuasselDatabase.Filtered
 import io.reactivex.Flowable
 import org.threeten.bp.Instant
 
-@Database(entities = [DatabaseMessage::class, Filtered::class], version = 6)
+@Database(entities = [DatabaseMessage::class, Filtered::class], version = 8)
 @TypeConverters(DatabaseMessage.MessageTypeConverters::class)
 abstract class QuasselDatabase : RoomDatabase() {
   abstract fun message(): MessageDao
   abstract fun filtered(): FilteredDao
 
-  @Entity(tableName = "message")
+  @Entity(tableName = "message", indices = [Index("bufferId"), Index("ignored")])
   data class DatabaseMessage(
     @PrimaryKey var messageId: Int,
     var time: Instant,
@@ -33,7 +32,8 @@ abstract class QuasselDatabase : RoomDatabase() {
     var senderPrefixes: String,
     var realName: String,
     var avatarUrl: String,
-    var content: String
+    var content: String,
+    var ignored: Boolean
   ) {
     class MessageTypeConverters {
       @TypeConverter
@@ -54,26 +54,18 @@ abstract class QuasselDatabase : RoomDatabase() {
 
   @Dao
   interface MessageDao {
+    @Query("SELECT * FROM message")
+    fun all(): List<DatabaseMessage>
+
     @Query("SELECT * FROM message WHERE messageId = :messageId")
     fun find(messageId: Int): DatabaseMessage?
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC")
     fun findByBufferId(bufferId: Int): List<DatabaseMessage>
 
-    @Query(
-      "SELECT * FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 ORDER BY messageId DESC"
-    )
+    @Query("SELECT * FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0 ORDER BY messageId DESC")
     fun findByBufferIdPaged(bufferId: Int, type: Int): DataSource.Factory<Int, DatabaseMessage>
 
-    @RawQuery(observedEntities = [DatabaseMessage::class])
-    fun findMessagesRawPaged(query: SupportSQLiteQuery): DataSource.Factory<Int, DatabaseMessage>
-
-    @RawQuery
-    fun findDaysRaw(query: SupportSQLiteQuery): List<Instant>
-
-    @RawQuery
-    fun findMessagesRaw(query: SupportSQLiteQuery): List<DatabaseMessage>
-
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1")
     fun findLastByBufferId(bufferId: Int): DatabaseMessage?
 
@@ -83,7 +75,7 @@ abstract class QuasselDatabase : RoomDatabase() {
     @Query("SELECT messageId FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC LIMIT 1")
     fun firstMsgId(bufferId: Int): Flowable<MsgId>
 
-    @Query("SELECT messageId FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 ORDER BY messageId ASC LIMIT 1")
+    @Query("SELECT messageId FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0 ORDER BY messageId ASC LIMIT 1")
     fun firstVisibleMsgId(bufferId: Int, type: Int): MsgId?
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC LIMIT 1")
@@ -189,6 +181,18 @@ abstract class QuasselDatabase : RoomDatabase() {
                   database.execSQL("drop table message;")
                   database.execSQL("create table message (messageId INTEGER not null primary key, time INTEGER not null, type INTEGER not null, flag INTEGER not null, bufferId INTEGER not null, sender TEXT not null, senderPrefixes TEXT not null, realName TEXT not null, avatarUrl TEXT not null, content TEXT not null);")
                 }
+              },
+              object : Migration(6, 7) {
+                override fun migrate(database: SupportSQLiteDatabase) {
+                  database.execSQL("drop table message;")
+                  database.execSQL("create table message (messageId INTEGER not null primary key, time INTEGER not null, type INTEGER not null, flag INTEGER not null, bufferId INTEGER not null, sender TEXT not null, senderPrefixes TEXT not null, realName TEXT not null, avatarUrl TEXT not null, content TEXT not null, ignored INTEGER not null);")
+                }
+              },
+              object : Migration(7, 8) {
+                override fun migrate(database: SupportSQLiteDatabase) {
+                  database.execSQL("CREATE INDEX index_message_bufferId ON message(bufferId);")
+                  database.execSQL("CREATE INDEX index_message_ignored ON message(ignored);")
+                }
               }
             ).build()
           }
-- 
GitLab