diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 4653d4b5b8..30ff7df4fa 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -28,6 +28,7 @@ android {
         targetSdkVersion 28
         versionCode 1
         versionName "1.0"
+        multiDexEnabled true
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 
     }
@@ -104,17 +105,17 @@ dependencies {
     testImplementation 'org.robolectric:shadows-support-v4:3.0'
     testImplementation "io.mockk:mockk:1.8.13.kotlin13"
     testImplementation 'org.amshove.kluent:kluent-android:1.44'
-    testImplementation "androidx.arch.core:core-testing:$lifecycle_version"
     testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
 
     androidTestImplementation "org.koin:koin-test:$koin_version"
+    androidTestImplementation 'androidx.test:core:1.1.0'
     androidTestImplementation 'androidx.test:runner:1.1.1'
     androidTestImplementation 'androidx.test:rules:1.1.1'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
     androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
     androidTestImplementation "io.mockk:mockk-android:1.8.13.kotlin13"
     androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version"
     androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
 
-
 }
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt
index 397dcfc89a..b76014d18b 100644
--- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt
@@ -16,10 +16,9 @@
 
 package im.vector.matrix.android.session.room.timeline
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.InstrumentedTest
-import im.vector.matrix.android.api.session.events.model.Event
-import im.vector.matrix.android.api.session.events.model.EventType
 import im.vector.matrix.android.internal.database.helper.add
 import im.vector.matrix.android.internal.database.helper.addAll
 import im.vector.matrix.android.internal.database.helper.isUnlinked
@@ -27,6 +26,9 @@ import im.vector.matrix.android.internal.database.helper.lastStateIndex
 import im.vector.matrix.android.internal.database.helper.merge
 import im.vector.matrix.android.internal.database.model.ChunkEntity
 import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
+import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeListOfEvents
+import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeMessageEvent
+import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeRoomMemberEvent
 import io.realm.Realm
 import io.realm.RealmConfiguration
 import io.realm.kotlin.createObject
@@ -35,9 +37,10 @@ import org.amshove.kluent.shouldBeTrue
 import org.amshove.kluent.shouldEqual
 import org.junit.Before
 import org.junit.Test
-import kotlin.random.Random
+import org.junit.runner.RunWith
 
 
+@RunWith(AndroidJUnit4::class)
 internal class ChunkEntityTest : InstrumentedTest {
 
     private lateinit var monarchy: Monarchy
@@ -54,7 +57,7 @@ internal class ChunkEntityTest : InstrumentedTest {
     fun add_shouldAdd_whenNotAlreadyIncluded() {
         monarchy.runTransactionSync { realm ->
             val chunk: ChunkEntity = realm.createObject()
-            val fakeEvent = createFakeEvent(false)
+            val fakeEvent = createFakeMessageEvent()
             chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
             chunk.events.size shouldEqual 1
         }
@@ -64,7 +67,7 @@ internal class ChunkEntityTest : InstrumentedTest {
     fun add_shouldNotAdd_whenAlreadyIncluded() {
         monarchy.runTransactionSync { realm ->
             val chunk: ChunkEntity = realm.createObject()
-            val fakeEvent = createFakeEvent(false)
+            val fakeEvent = createFakeMessageEvent()
             chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
             chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
             chunk.events.size shouldEqual 1
@@ -75,7 +78,7 @@ internal class ChunkEntityTest : InstrumentedTest {
     fun add_shouldStateIndexIncremented_whenStateEventIsAddedForward() {
         monarchy.runTransactionSync { realm ->
             val chunk: ChunkEntity = realm.createObject()
-            val fakeEvent = createFakeEvent(true)
+            val fakeEvent = createFakeRoomMemberEvent()
             chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
             chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 1
         }
@@ -85,7 +88,7 @@ internal class ChunkEntityTest : InstrumentedTest {
     fun add_shouldStateIndexNotIncremented_whenNoStateEventIsAdded() {
         monarchy.runTransactionSync { realm ->
             val chunk: ChunkEntity = realm.createObject()
-            val fakeEvent = createFakeEvent(false)
+            val fakeEvent = createFakeMessageEvent()
             chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
             chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 0
         }
@@ -196,15 +199,4 @@ internal class ChunkEntityTest : InstrumentedTest {
         }
     }
 
-
-    private fun createFakeListOfEvents(size: Int = 10): List<Event> {
-        return (0 until size).map { createFakeEvent(Random.nextBoolean()) }
-    }
-
-    private fun createFakeEvent(asStateEvent: Boolean = false): Event {
-        val eventId = Random.nextLong(System.currentTimeMillis()).toString()
-        val type = if (asStateEvent) EventType.STATE_ROOM_NAME else EventType.MESSAGE
-        return Event(type, eventId)
-    }
-
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt
index 35e9cb12ec..9d2edd745f 100644
--- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt
@@ -17,9 +17,15 @@
 package im.vector.matrix.android.session.room.timeline
 
 import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.api.session.events.model.Content
 import im.vector.matrix.android.api.session.events.model.Event
 import im.vector.matrix.android.api.session.events.model.EventType
+import im.vector.matrix.android.api.session.events.model.toContent
+import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.api.session.room.model.MyMembership
+import im.vector.matrix.android.api.session.room.model.RoomMember
+import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
+import im.vector.matrix.android.api.session.room.model.message.MessageType
 import im.vector.matrix.android.internal.database.helper.addAll
 import im.vector.matrix.android.internal.database.helper.addOrUpdate
 import im.vector.matrix.android.internal.database.model.ChunkEntity
@@ -30,27 +36,56 @@ import kotlin.random.Random
 
 object RoomDataHelper {
 
+    private const val FAKE_TEST_SENDER = "@sender:test.org"
+    private val EVENT_FACTORIES = hashMapOf(
+            0 to { createFakeMessageEvent() },
+            1 to { createFakeRoomMemberEvent() }
+    )
+
     fun createFakeListOfEvents(size: Int = 10): List<Event> {
-        return (0 until size).map { createFakeEvent(Random.nextBoolean()) }
+        return (0 until size).mapNotNull {
+            val nextInt = Random.nextInt(EVENT_FACTORIES.size)
+            EVENT_FACTORIES[nextInt]?.invoke()
+        }
     }
 
-    fun createFakeEvent(asStateEvent: Boolean = false): Event {
-        val eventId = Random.nextLong(System.currentTimeMillis()).toString()
-        val type = if (asStateEvent) EventType.STATE_ROOM_NAME else EventType.MESSAGE
-        return Event(type, eventId)
+    fun createFakeEvent(type: String,
+                        content: Content? = null,
+                        prevContent: Content? = null,
+                        sender: String = FAKE_TEST_SENDER,
+                        stateKey: String = FAKE_TEST_SENDER
+    ): Event {
+        return Event(
+                type = type,
+                eventId = Random.nextLong().toString(),
+                content = content,
+                prevContent = prevContent,
+                sender = sender,
+                stateKey = stateKey
+        )
+    }
+
+    fun createFakeMessageEvent(): Event {
+        val message = MessageTextContent(MessageType.MSGTYPE_TEXT, "Fake message #${Random.nextLong()}").toContent()
+        return createFakeEvent(EventType.MESSAGE, message)
+    }
+
+    fun createFakeRoomMemberEvent(): Event {
+        val roomMember = RoomMember(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
+        return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
     }
 
     fun fakeInitialSync(monarchy: Monarchy, roomId: String) {
         monarchy.runTransactionSync { realm ->
             val roomEntity = realm.createObject<RoomEntity>(roomId)
             roomEntity.membership = MyMembership.JOINED
-            val eventList = createFakeListOfEvents(30)
+            val eventList = createFakeListOfEvents(10)
             val chunkEntity = realm.createObject<ChunkEntity>().apply {
                 nextToken = null
                 prevToken = Random.nextLong(System.currentTimeMillis()).toString()
                 isLastForward = true
             }
-            chunkEntity.addAll("roomId", eventList, PaginationDirection.FORWARDS)
+            chunkEntity.addAll(roomId, eventList, PaginationDirection.FORWARDS)
             roomEntity.addOrUpdate(chunkEntity)
         }
     }
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineHolderTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt
similarity index 50%
rename from matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineHolderTest.kt
rename to matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt
index 2063a61836..a80e92a098 100644
--- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineHolderTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt
@@ -16,55 +16,75 @@
 
 package im.vector.matrix.android.session.room.timeline
 
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.test.annotation.UiThreadTest
 import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.InstrumentedTest
-import im.vector.matrix.android.LiveDataTestObserver
+import im.vector.matrix.android.api.session.room.timeline.Timeline
+import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor
-import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService
+import im.vector.matrix.android.internal.session.room.timeline.DefaultTimeline
+import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory
 import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.testCoroutineDispatchers
 import io.realm.Realm
 import io.realm.RealmConfiguration
+import org.amshove.kluent.shouldEqual
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
+import timber.log.Timber
+import java.util.concurrent.CountDownLatch
 
-internal class TimelineHolderTest : InstrumentedTest {
+internal class TimelineTest : InstrumentedTest {
+
+    companion object {
+        private const val ROOM_ID = "roomId"
+    }
 
-    @get:Rule val testRule = InstantTaskExecutorRule()
     private lateinit var monarchy: Monarchy
 
     @Before
     fun setup() {
+        Timber.plant(Timber.DebugTree())
         Realm.init(context())
         val testConfiguration = RealmConfiguration.Builder().name("test-realm").build()
         Realm.deleteRealm(testConfiguration)
         monarchy = Monarchy.Builder().setRealmConfiguration(testConfiguration).build()
+        RoomDataHelper.fakeInitialSync(monarchy, ROOM_ID)
     }
 
-    @Test
-    @UiThreadTest
-    fun backPaginate_shouldLoadMoreEvents_whenLoadAroundIsCalled() {
-        val roomId = "roomId"
+    private fun createTimeline(initialEventId: String? = null): Timeline {
         val taskExecutor = TaskExecutor(testCoroutineDispatchers)
         val tokenChunkEventPersistor = TokenChunkEventPersistor(monarchy)
         val paginationTask = FakePaginationTask(tokenChunkEventPersistor)
         val getContextOfEventTask = FakeGetContextOfEventTask(tokenChunkEventPersistor)
-        RoomDataHelper.fakeInitialSync(monarchy, roomId)
-        val timelineHolder = DefaultTimelineService(roomId, monarchy, taskExecutor, getContextOfEventTask, RoomMemberExtractor(roomId))
-        val timelineObserver = LiveDataTestObserver.test(timelineHolder.timeline())
-        timelineObserver.awaitNextValue().assertHasValue()
-        var timelineData = timelineObserver.value()
-        timelineData.events.size shouldEqual 30
-        (0 until timelineData.events.size).map {
-            timelineData.events.loadAround(it)
+        val roomMemberExtractor = RoomMemberExtractor(ROOM_ID)
+        val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
+        return DefaultTimeline(ROOM_ID, initialEventId, monarchy.realmConfiguration, taskExecutor, getContextOfEventTask, timelineEventFactory, paginationTask, null)
+    }
+
+    @Test
+    fun backPaginate_shouldLoadMoreEvents_whenPaginateIsCalled() {
+        val timeline = createTimeline()
+        timeline.start()
+        val paginationCount = 30
+        var initialLoad = 0
+        val latch = CountDownLatch(2)
+        var timelineEvents: List<TimelineEvent> = emptyList()
+        timeline.listener = object : Timeline.Listener {
+            override fun onUpdated(snapshot: List<TimelineEvent>) {
+                if (snapshot.isNotEmpty()) {
+                    if (initialLoad == 0) {
+                        initialLoad = snapshot.size
+                    }
+                    timelineEvents = snapshot
+                    latch.countDown()
+                    timeline.paginate(Timeline.Direction.BACKWARDS, paginationCount)
+                }
+            }
         }
-        timelineObserver.awaitNextValue().assertHasValue()
-        timelineData = timelineObserver.value()
-        timelineData.events.size shouldEqual 60
+        latch.await()
+        timelineEvents.size shouldEqual initialLoad + paginationCount
+        timeline.dispose()
     }
 
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt
index 6d99c9eb50..9a66ffed62 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt
@@ -35,6 +35,18 @@ inline fun <reified T> Content?.toModel(): T? {
     }
 }
 
+/**
+ * This methods is a facility method to map a model to a json Content
+ */
+@Suppress("UNCHECKED_CAST")
+inline fun <reified T> T?.toContent(): Content? {
+    return this?.let {
+        val moshi = MoshiProvider.providesMoshi()
+        val moshiAdapter = moshi.adapter(T::class.java)
+        return moshiAdapter.toJsonValue(it) as Content
+    }
+}
+
 /**
  * Generic event class with all possible fields for events.
  * The content and prevContent json fields can easily be mapped to a model with [toModel] method.
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt
index 9a9e4b7cfe..b92598ab76 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt
@@ -54,10 +54,14 @@ internal fun ChunkEntity.merge(roomId: String,
     if (direction == PaginationDirection.FORWARDS) {
         this.nextToken = chunkToMerge.nextToken
         this.isLastForward = chunkToMerge.isLastForward
+        this.forwardsStateIndex = chunkToMerge.forwardsStateIndex
+        this.forwardsDisplayIndex = chunkToMerge.forwardsDisplayIndex
         eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
     } else {
         this.prevToken = chunkToMerge.prevToken
         this.isLastBackward = chunkToMerge.isLastBackward
+        this.backwardsStateIndex = chunkToMerge.backwardsStateIndex
+        this.backwardsDisplayIndex = chunkToMerge.backwardsDisplayIndex
         eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
     }
     eventsToMerge.forEach {
@@ -111,8 +115,7 @@ internal fun ChunkEntity.add(roomId: String,
         this.displayIndex = currentDisplayIndex
     }
     // We are not using the order of the list, but will be sorting with displayIndex field
-    val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
-    events.add(position, eventEntity)
+    events.add(eventEntity)
 }
 
 private fun ChunkEntity.assertIsManaged() {
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersTask.kt
index 7cd8bbafb9..c421b3bd3d 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersTask.kt
@@ -48,7 +48,6 @@ internal class DefaultLoadRoomMembersTask(private val roomAPI: RoomAPI,
         return if (areAllMembersAlreadyLoaded(params.roomId)) {
             Try.just(true)
         } else {
-            //TODO use this token
             val lastToken = syncTokenStore.getLastToken()
             executeRequest<RoomMembersResponse> {
                 apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value)
@@ -63,7 +62,7 @@ internal class DefaultLoadRoomMembersTask(private val roomAPI: RoomAPI,
                 .tryTransactionSync { realm ->
                     // We ignore all the already known members
                     val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                            ?: realm.createObject(roomId)
+                                     ?: realm.createObject(roomId)
 
                     val roomMembers = RoomMembers(realm, roomId).getLoaded()
                     val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }
@@ -78,9 +77,9 @@ internal class DefaultLoadRoomMembersTask(private val roomAPI: RoomAPI,
 
     private fun areAllMembersAlreadyLoaded(roomId: String): Boolean {
         return monarchy
-                .fetchAllCopiedSync { RoomEntity.where(it, roomId) }
-                .firstOrNull()
-                ?.areAllMembersLoaded ?: false
+                       .fetchAllCopiedSync { RoomEntity.where(it, roomId) }
+                       .firstOrNull()
+                       ?.areAllMembersLoaded ?: false
     }
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt
index ef037ac523..38555d2027 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt
@@ -36,7 +36,12 @@ import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoo
 import im.vector.matrix.android.internal.database.query.where
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.internal.task.configureWith
-import io.realm.*
+import io.realm.OrderedRealmCollectionChangeListener
+import io.realm.Realm
+import io.realm.RealmConfiguration
+import io.realm.RealmQuery
+import io.realm.RealmResults
+import io.realm.Sort
 import timber.log.Timber
 import java.util.*
 import java.util.concurrent.atomic.AtomicBoolean
@@ -97,10 +102,14 @@ internal class DefaultTimeline(
             val state = getPaginationState(direction)
             if (state.isPaginating) {
                 // We are getting new items from pagination
-                paginateInternal(startDisplayIndex, direction, state.requestedCount)
+                val shouldPostSnapshot = paginateInternal(startDisplayIndex, direction, state.requestedCount)
+                if (shouldPostSnapshot) {
+                    postSnapshot()
+                }
             } else {
                 // We are getting new items from sync
                 buildTimelineEvents(startDisplayIndex, direction, range.length.toLong())
+                postSnapshot()
             }
         }
     }
@@ -114,7 +123,10 @@ internal class DefaultTimeline(
             }
             Timber.v("Paginate $direction of $count items")
             val startDisplayIndex = if (direction == Timeline.Direction.BACKWARDS) prevDisplayIndex else nextDisplayIndex
-            paginateInternal(startDisplayIndex, direction, count)
+            val shouldPostSnapshot = paginateInternal(startDisplayIndex, direction, count)
+            if (shouldPostSnapshot) {
+                postSnapshot()
+            }
         }
     }
 
@@ -191,13 +203,15 @@ internal class DefaultTimeline(
 
     /**
      * This has to be called on TimelineThread as it access realm live results
+     * @return true if snapshot should be posted
      */
     private fun paginateInternal(startDisplayIndex: Int,
                                  direction: Timeline.Direction,
-                                 count: Int) {
+                                 count: Int): Boolean {
         updatePaginationState(direction) { it.copy(requestedCount = count, isPaginating = true) }
         val builtCount = buildTimelineEvents(startDisplayIndex, direction, count.toLong())
-        if (builtCount < count && !hasReachedEnd(direction)) {
+        val shouldFetchMore = builtCount < count && !hasReachedEnd(direction)
+        if (shouldFetchMore) {
             val newRequestedCount = count - builtCount
             updatePaginationState(direction) { it.copy(requestedCount = newRequestedCount) }
             val fetchingCount = Math.max(MIN_FETCHING_COUNT, newRequestedCount)
@@ -205,6 +219,7 @@ internal class DefaultTimeline(
         } else {
             updatePaginationState(direction) { it.copy(isPaginating = false, requestedCount = 0) }
         }
+        return !shouldFetchMore
     }
 
     private fun snapshot(): List<TimelineEvent> {
@@ -252,12 +267,13 @@ internal class DefaultTimeline(
         } else {
             val count = Math.min(INITIAL_LOAD_SIZE, liveEvents.size)
             if (isLive) {
-                paginate(Timeline.Direction.BACKWARDS, count)
+                paginateInternal(initialDisplayIndex, Timeline.Direction.BACKWARDS, count)
             } else {
-                paginate(Timeline.Direction.FORWARDS, count / 2)
-                paginate(Timeline.Direction.BACKWARDS, count / 2)
+                paginateInternal(initialDisplayIndex, Timeline.Direction.FORWARDS, count / 2)
+                paginateInternal(initialDisplayIndex, Timeline.Direction.BACKWARDS, count / 2)
             }
         }
+        postSnapshot()
     }
 
     /**
@@ -266,9 +282,9 @@ internal class DefaultTimeline(
     private fun executePaginationTask(direction: Timeline.Direction, limit: Int) {
         val token = getTokenLive(direction) ?: return
         val params = PaginationTask.Params(roomId = roomId,
-                from = token,
-                direction = direction.toPaginationDirection(),
-                limit = limit)
+                                           from = token,
+                                           direction = direction.toPaginationDirection(),
+                                           limit = limit)
 
         Timber.v("Should fetch $limit items $direction")
         paginationTask.configureWith(params)
@@ -336,8 +352,6 @@ internal class DefaultTimeline(
             builtEvents.add(position, timelineEvent)
         }
         Timber.v("Built ${offsetResults.size} items from db")
-        val snapshot = snapshot()
-        mainHandler.post { listener?.onUpdated(snapshot) }
         return offsetResults.size
     }
 
@@ -399,6 +413,11 @@ internal class DefaultTimeline(
         contextOfEventTask.configureWith(params).executeBy(taskExecutor)
     }
 
+    private fun postSnapshot() {
+        val snapshot = snapshot()
+        mainHandler.post { listener?.onUpdated(snapshot) }
+    }
+
 // Extension methods ***************************************************************************
 
     private fun Timeline.Direction.toPaginationDirection(): PaginationDirection {