diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7ee4ddaa96bb90e46b4b5c0ae890b51d6a205da1
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt
@@ -0,0 +1,63 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.service
+
+import de.kuschku.libquassel.protocol.BufferId
+import de.kuschku.libquassel.protocol.Message
+import de.kuschku.libquassel.protocol.MsgId
+import de.kuschku.libquassel.util.helpers.value
+import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
+
+class BacklogRequester(
+  private val viewModel: QuasselViewModel,
+  private val database: QuasselDatabase
+) {
+  fun loadMore(accountId: Long, buffer: BufferId, amount: Int, pageSize: Int,
+               lastMessageId: MsgId? = null,
+               untilVisible: Boolean = false,
+               finishCallback: () -> Unit) {
+    var missing = amount
+    viewModel.session.value?.orNull()?.backlogManager?.let {
+      it.requestBacklog(
+        bufferId = buffer,
+        last = lastMessageId ?: database.message().findFirstByBufferId(
+          buffer
+        )?.messageId ?: -1,
+        limit = amount
+      ) {
+        if (untilVisible && it.isNotEmpty()) {
+          val filtered = database.filtered().get(accountId, buffer) ?: 0
+          missing -= it.count {
+            (it.type.value and filtered.inv()) != 0
+          }
+          val messageId = it.map(Message::messageId).min()
+          if (missing > 0) {
+            loadMore(accountId, buffer, missing, pageSize, messageId, untilVisible, finishCallback)
+          } else {
+            finishCallback()
+          }
+        } else {
+          finishCallback()
+        }
+      }
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
index 80456bf453a568563f63c952cc2227b196d89216..b9a452c90650f1f69fead6fdf74fc82d67d192e3 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
@@ -107,7 +107,9 @@ class QuasselNotificationBackend @Inject constructor(
                                 Message_Type.Action,
                                 Message_Type.Notice).toInt(),
                 0
-              )
+              ) {
+                processMessages(session, *it.toTypedArray())
+              }
           }
           NotificationSettings.Level.HIGHLIGHT -> {
             val highlightCount = session.bufferSyncer.highlightCount(buffer.bufferId)
@@ -118,7 +120,9 @@ class QuasselNotificationBackend @Inject constructor(
                                 Message_Type.Action,
                                 Message_Type.Notice).toInt(),
                 Message_Flag.of(Message_Flag.Highlight).toInt()
-              )
+              ) {
+                processMessages(session, *it.toTypedArray())
+              }
             }
           }
           NotificationSettings.Level.NONE      -> {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
index 46c74aa9a2c19ae9cafcd8072bc52d948c9a3476..2784a57f4b6ba8cebd7dad4d5c99cbc9301adee1 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
@@ -51,6 +51,7 @@ import de.kuschku.libquassel.util.irc.HostmaskHelper
 import de.kuschku.quasseldroid.GlideApp
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.service.BacklogRequester
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.BacklogSettings
@@ -100,6 +101,8 @@ class MessageListFragment : ServiceBoundFragment() {
 
   private lateinit var linearLayoutManager: LinearLayoutManager
 
+  private lateinit var backlogRequester: BacklogRequester
+
   private var lastBuffer: BufferId? = null
   private var previousMessageId: MsgId? = null
 
@@ -207,6 +210,8 @@ class MessageListFragment : ServiceBoundFragment() {
     linearLayoutManager = LinearLayoutManager(context)
     linearLayoutManager.reverseLayout = true
 
+    backlogRequester = BacklogRequester(viewModel, database)
+
     adapter.setOnClickListener { msg ->
       if (actionMode != null) {
         if (!viewModel.selectedMessagesToggle(msg.id, msg)) {
@@ -481,14 +486,19 @@ class MessageListFragment : ServiceBoundFragment() {
         if (bufferId > 0 && bufferId != Int.MAX_VALUE) {
           if (initial) swipeRefreshLayout.isRefreshing = true
           runInBackground {
-            viewModel.session {
-              it.orNull()?.backlogManager?.requestBacklog(
-                bufferId = bufferId,
-                last = lastMessageId ?: database.message().findFirstByBufferId(
-                  bufferId
-                )?.messageId ?: -1,
-                limit = if (initial) backlogSettings.initialAmount else backlogSettings.pageSize
-              )
+            backlogRequester.loadMore(
+              accountId = accountId,
+              buffer = bufferId,
+              amount = if (initial) backlogSettings.initialAmount else backlogSettings.pageSize,
+              pageSize = backlogSettings.pageSize,
+              lastMessageId = lastMessageId
+                              ?: database.message().findFirstByBufferId(bufferId)?.messageId ?: -1,
+              untilVisible = initial
+            ) {
+              Throwable().printStackTrace()
+              requireActivity().runOnUiThread {
+                swipeRefreshLayout.isRefreshing = 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 f8fcdbfc8b3bc052ab0318d24dbd149b62576bf5..8dd80345cfd4b0f3c5ea038b1b816bd9c8b2ff42 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
@@ -30,19 +30,8 @@ class BacklogManager(
   private val notificationManager: NotificationManager?,
   private val backlogStorage: BacklogStorage
 ) : SyncableObject(session, "BacklogManager"), IBacklogManager {
-  override fun receiveBacklogFiltered(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int,
-                                      additional: Int, type: Int, flags: Int,
-                                      messages: QVariantList) {
-    val actualMessages = messages.mapNotNull { it.value<Message?>(null) }
-    notificationManager?.processMessages(
-      session, *actualMessages.toTypedArray()
-    )
-  }
-
-  override fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int,
-                                         type: Int, flags: Int, messages: QVariantList) {
-    // TODO: Not implemented
-  }
+  private val loading = mutableMapOf<BufferId, (List<Message>) -> Unit>()
+  private val loadingFiltered = mutableMapOf<BufferId, (List<Message>) -> Unit>()
 
   init {
     initialized = true
@@ -50,14 +39,57 @@ class BacklogManager(
 
   fun updateIgnoreRules() = backlogStorage.updateIgnoreRules(session)
 
+  fun requestBacklog(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
+                     additional: Int = 0, callback: (List<Message>) -> Unit) {
+    if (loading.contains(bufferId)) return
+    loading[bufferId] = callback
+    requestBacklog(bufferId, first, last, limit, additional)
+  }
+
+  fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1,
+                             limit: Int = -1, additional: Int = 0, type: Int = -1, flags: Int = -1,
+                             callback: (List<Message>) -> Unit) {
+    if (loadingFiltered.contains(bufferId)) return
+    loadingFiltered[bufferId] = callback
+    requestBacklogFiltered(bufferId, first, last, limit, additional, type, flags)
+  }
+
+  fun requestBacklogAll(first: MsgId = -1, last: MsgId = -1, limit: Int = -1, additional: Int = 0,
+                        callback: (List<Message>) -> Unit) {
+    if (loading.contains(-1)) return
+    loading[-1] = callback
+    requestBacklogAll(first, last, limit, additional)
+  }
+
+  fun requestBacklogAllFiltered(first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
+                                additional: Int = 0, type: Int = -1, flags: Int = -1,
+                                callback: (List<Message>) -> Unit) {
+    if (loading.contains(-1)) return
+    loadingFiltered[-1] = callback
+    requestBacklogAllFiltered(first, last, limit, additional, type, flags)
+  }
+
   override fun receiveBacklog(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int,
                               additional: Int, messages: QVariantList) {
     backlogStorage.storeMessages(session, messages.mapNotNull(QVariant_::value), initialLoad = true)
+    loading.remove(bufferId)?.invoke(messages.mapNotNull { it.value<Message?>(null) })
   }
 
   override fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int,
                                  messages: QVariantList) {
     backlogStorage.storeMessages(session, messages.mapNotNull(QVariant_::value), initialLoad = true)
+    loading.remove(-1)?.invoke(messages.mapNotNull { it.value<Message?>(null) })
+  }
+
+  override fun receiveBacklogFiltered(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int,
+                                      additional: Int, type: Int, flags: Int,
+                                      messages: QVariantList) {
+    loadingFiltered.remove(bufferId)?.invoke(messages.mapNotNull { it.value<Message?>(null) })
+  }
+
+  override fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int,
+                                         type: Int, flags: Int, messages: QVariantList) {
+    loadingFiltered.remove(-1)?.invoke(messages.mapNotNull { it.value<Message?>(null) })
   }
 
   fun removeBuffer(buffer: BufferId) {