From 9cc3dc51ccac46cad3aed80471ef482d9a91d667 Mon Sep 17 00:00:00 2001
From: ganfra <francois.ganard@gmail.com>
Date: Tue, 6 Nov 2018 16:35:06 +0100
Subject: [PATCH] Use Try instead of Either as it makes more sens + add
 GroupRooms API

---
 .idea/caches/build_file_checksums.ser         | Bin 661 -> 659 bytes
 .../features/login/LoginActivity.kt           |   3 +-
 matrix-sdk-android/build.gradle               |   8 +-
 .../matrix/android/api/MatrixCallback.kt      |   4 +-
 .../api/session/group/model/GroupSummary.kt   |   3 +-
 .../matrix/android/internal/auth/AuthAPI.kt   |   5 +-
 .../internal/auth/DefaultAuthenticator.kt     |   9 +-
 .../internal/auth/SessionParamsStore.kt       |   3 +-
 .../auth/db/RealmSessionParamsStore.kt        |  18 ++--
 .../database/mapper/GroupSummaryMapper.kt     |   3 +-
 .../database/model/GroupSummaryEntity.kt      |   4 +-
 .../android/internal/di/MatrixModule.kt       |   1 -
 .../android/internal/di/NetworkModule.kt      |   7 --
 .../android/internal/network/Request.kt       |  58 ++++++------
 .../session/group/GetGroupDataRequest.kt      |  70 ++++++++++++++
 .../session/group/GetGroupSummaryRequest.kt   |  68 --------------
 .../internal/session/group/GroupAPI.kt        |  14 ++-
 .../internal/session/group/GroupModule.kt     |   2 +-
 .../session/group/GroupSummaryUpdater.kt      |   5 +-
 .../internal/session/group/model/GroupRoom.kt |  19 ++++
 .../session/group/model/GroupRooms.kt         |  12 +++
 .../android/internal/session/room/RoomAPI.kt  |   5 +-
 .../room/members/LoadRoomMembersRequest.kt    |  47 ++++------
 .../room/timeline/PaginationRequest.kt        |  88 ++++++++----------
 .../room/timeline/TimelineBoundaryCallback.kt |   3 +-
 .../android/internal/session/sync/SyncAPI.kt  |   7 +-
 .../internal/session/sync/SyncRequest.kt      |  17 +---
 .../session/sync/SyncResponseHandler.kt       |  28 +++---
 .../internal/session/sync/job/SyncThread.kt   |   2 +-
 .../matrix/android/internal/util/Monarchy.kt  |  17 ++++
 30 files changed, 276 insertions(+), 254 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataRequest.kt
 delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt
 create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRoom.kt
 create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRooms.kt
 create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt

diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index d4c39de25406e077818d761a58916cc0c7bf4cdc..a2a0d2fa860743dce0c65ec45cd5c18b0b96066e 100644
GIT binary patch
delta 86
zcmV-c0IC0#1(OAkm;~vo6B&`5hz=(&RdZ!>b1!FMZf0^}lLZ1clOzFY5UBmQ8*stm
sM-9<bAV+NgmXn<UMiYP5%%GHYJ<Ji|te}22fqHZR5t9xA8<ST8KB5{S4gdfE

delta 88
zcmV-e0H^<x1(gMmm;~jF$}y3gh?8gmX%Ixwon1`a*n8P_?CWde_yv>U0Y)O7Gu;y6
ujW3A}?37~hAqh@&04Xn3b7gXKFJ^LKZewq0bCWUwHUSZnI{_P$RRTT?o+OO`

diff --git a/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt b/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt
index 2314be29d2..e3acb888e0 100644
--- a/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt
+++ b/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt
@@ -6,7 +6,6 @@ import android.widget.Toast
 import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
-import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.RiotActivity
@@ -43,7 +42,7 @@ class LoginActivity : RiotActivity() {
                 goToHome()
             }
 
-            override fun onFailure(failure: Failure) {
+            override fun onFailure(failure: Throwable) {
                 progressBar.visibility = View.GONE
                 Toast.makeText(this@LoginActivity, "Authenticate failure: $failure", Toast.LENGTH_LONG).show()
             }
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index f619b79cde..5b5d5c7aca 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -41,13 +41,14 @@ android {
 
 dependencies {
 
-    def arrow_version = "0.7.3"
+    def arrow_version = "0.8.0"
     def support_version = '28.0.0'
     def moshi_version = '1.7.0'
     def lifecycle_version = "1.1.1"
 
     implementation fileTree(dir: 'libs', include: ['*.aar'])
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
 
     implementation "com.android.support:appcompat-v7:$support_version"
     implementation "com.android.support:recyclerview-v7:$support_version"
@@ -59,7 +60,6 @@ dependencies {
     implementation 'com.squareup.retrofit2:retrofit:2.4.0'
     implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
     implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
-    implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
     implementation 'com.squareup.okhttp3:okhttp:3.10.0'
     implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
     implementation 'com.squareup.okio:okio:1.15.0'
@@ -77,6 +77,10 @@ dependencies {
 
     // FP
     implementation "io.arrow-kt:arrow-core:$arrow_version"
+    implementation "io.arrow-kt:arrow-instances-core:$arrow_version"
+    implementation "io.arrow-kt:arrow-effects:$arrow_version"
+    implementation "io.arrow-kt:arrow-effects-instances:$arrow_version"
+    implementation "io.arrow-kt:arrow-integration-retrofit-adapter:$arrow_version"
 
     // DI
     implementation "org.koin:koin-core:$koin_version"
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt
index db85de5ccf..ee4a2ac6b2 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt
@@ -1,14 +1,12 @@
 package im.vector.matrix.android.api
 
-import im.vector.matrix.android.api.failure.Failure
-
 interface MatrixCallback<in T> {
 
     fun onSuccess(data: T) {
         //no-op
     }
 
-    fun onFailure(failure: Failure){
+    fun onFailure(failure: Throwable) {
         //no-op
     }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt
index 115dbb074c..2ae1d2feb6 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt
@@ -4,5 +4,6 @@ data class GroupSummary(
         val groupId: String,
         val displayName: String = "",
         val shortDescription: String = "",
-        val avatarUrl: String = ""
+        val avatarUrl: String = "",
+        val roomIds: List<String> = emptyList()
 )
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt
index 3e4b1139f9..0a66afb507 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt
@@ -3,8 +3,7 @@ package im.vector.matrix.android.internal.auth
 import im.vector.matrix.android.internal.auth.data.Credentials
 import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
 import im.vector.matrix.android.internal.network.NetworkConstants
-import kotlinx.coroutines.Deferred
-import retrofit2.Response
+import retrofit2.Call
 import retrofit2.http.Body
 import retrofit2.http.POST
 
@@ -19,6 +18,6 @@ interface AuthAPI {
      * @param loginParams the login parameters
      */
     @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
-    fun login(@Body loginParams: PasswordLoginParams): Deferred<Response<Credentials>>
+    fun login(@Body loginParams: PasswordLoginParams): Call<Credentials>
 
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt
index 21ddbe3c32..2dd7f6e323 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt
@@ -1,12 +1,9 @@
 package im.vector.matrix.android.internal.auth
 
 import android.util.Patterns
-import arrow.core.Either
-import arrow.core.leftIfNull
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.auth.Authenticator
 import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
-import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.internal.auth.data.Credentials
@@ -44,7 +41,7 @@ class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
 
         val job = GlobalScope.launch(coroutineDispatchers.main) {
             val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password)
-            sessionOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
+            sessionOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) })
         }
         return CancelableCoroutine(job)
 
@@ -52,7 +49,7 @@ class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
 
     private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
                                      login: String,
-                                     password: String): Either<Failure, Session> = withContext(coroutineDispatchers.io) {
+                                     password: String) = withContext(coroutineDispatchers.io) {
 
         val authAPI = buildAuthAPI(homeServerConnectionConfig)
         val loginParams = if (Patterns.EMAIL_ADDRESS.matcher(login).matches()) {
@@ -62,8 +59,6 @@ class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
         }
         executeRequest<Credentials> {
             apiCall = authAPI.login(loginParams)
-        }.leftIfNull {
-            Failure.Unknown(IllegalArgumentException("Credentials shouldn't not be null"))
         }.map {
             val sessionParams = SessionParams(it, homeServerConnectionConfig)
             sessionParamsStore.save(sessionParams)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt
index 0108dea57f..e8c60549b4 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt
@@ -1,11 +1,12 @@
 package im.vector.matrix.android.internal.auth
 
+import arrow.core.Try
 import im.vector.matrix.android.internal.auth.data.SessionParams
 
 interface SessionParamsStore {
 
     fun get(): SessionParams?
 
-    fun save(sessionParams: SessionParams)
+    fun save(sessionParams: SessionParams): Try<SessionParams>
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt
index 6c3241c081..0adceee44d 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt
@@ -1,5 +1,6 @@
 package im.vector.matrix.android.internal.auth.db
 
+import arrow.core.Try
 import im.vector.matrix.android.internal.auth.SessionParamsStore
 import im.vector.matrix.android.internal.auth.data.SessionParams
 import io.realm.Realm
@@ -8,14 +9,17 @@ import io.realm.RealmConfiguration
 class RealmSessionParamsStore(private val mapper: SessionParamsMapper,
                               private val realmConfiguration: RealmConfiguration) : SessionParamsStore {
 
-    override fun save(sessionParams: SessionParams) {
-        val entity = mapper.map(sessionParams)
-        if (entity != null) {
-            val realm = Realm.getInstance(realmConfiguration)
-            realm.executeTransaction {
-                it.insert(entity)
+    override fun save(sessionParams: SessionParams): Try<SessionParams> {
+        return Try {
+            val entity = mapper.map(sessionParams)
+            if (entity != null) {
+                val realm = Realm.getInstance(realmConfiguration)
+                realm.executeTransaction {
+                    it.insert(entity)
+                }
+                realm.close()
             }
-            realm.close()
+            sessionParams
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt
index 462419502b..0daa94155a 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt
@@ -11,7 +11,8 @@ object GroupSummaryMapper {
                 roomSummaryEntity.groupId,
                 roomSummaryEntity.displayName,
                 roomSummaryEntity.shortDescription,
-                roomSummaryEntity.avatarUrl
+                roomSummaryEntity.avatarUrl,
+                roomSummaryEntity.roomIds.toList()
         )
     }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt
index 45eb0e1c74..4c69616d50 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt
@@ -1,12 +1,14 @@
 package im.vector.matrix.android.internal.database.model
 
+import io.realm.RealmList
 import io.realm.RealmObject
 import io.realm.annotations.PrimaryKey
 
 open class GroupSummaryEntity(@PrimaryKey var groupId: String = "",
                               var displayName: String = "",
                               var shortDescription: String = "",
-                              var avatarUrl: String = ""
+                              var avatarUrl: String = "",
+                              var roomIds: RealmList<String> = RealmList()
 ) : RealmObject() {
 
     companion object
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt
index cee73a5ea0..d5dfd618e7 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt
@@ -5,7 +5,6 @@ import im.vector.matrix.android.api.thread.MainThreadExecutor
 import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
 import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.IO
 import kotlinx.coroutines.asCoroutineDispatcher
 import org.koin.dsl.context.ModuleDefinition
 import org.koin.dsl.module.Module
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt
index 04a97ea05d..69f2c68f0f 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt
@@ -1,6 +1,5 @@
 package im.vector.matrix.android.internal.di
 
-import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
 import im.vector.matrix.android.internal.network.AccessTokenInterceptor
 import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
 import okhttp3.OkHttpClient
@@ -8,7 +7,6 @@ import okhttp3.logging.HttpLoggingInterceptor
 import org.koin.dsl.context.ModuleDefinition
 import org.koin.dsl.module.Module
 import org.koin.dsl.module.module
-import retrofit2.CallAdapter
 import retrofit2.Converter
 import retrofit2.Retrofit
 import retrofit2.converter.moshi.MoshiConverterFactory
@@ -48,10 +46,6 @@ class NetworkModule : Module {
             MoshiConverterFactory.create(get()) as Converter.Factory
         }
 
-        single {
-            CoroutineCallAdapterFactory() as CallAdapter.Factory
-        }
-
         single {
             NetworkConnectivityChecker(get())
         }
@@ -60,7 +54,6 @@ class NetworkModule : Module {
             Retrofit.Builder()
                     .client(get())
                     .addConverterFactory(get())
-                    .addCallAdapterFactory(get())
         }
 
     }.invoke()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt
index f34d360191..eaeaff5258 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt
@@ -1,54 +1,50 @@
 package im.vector.matrix.android.internal.network
 
-import arrow.core.Either
+import arrow.core.Try
+import arrow.core.failure
+import arrow.core.recoverWith
+import arrow.effects.IO
+import arrow.effects.fix
+import arrow.effects.instances.io.async.async
+import arrow.integrations.retrofit.adapter.runAsync
 import com.squareup.moshi.Moshi
 import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.failure.MatrixError
 import im.vector.matrix.android.internal.di.MoshiProvider
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.coroutineScope
 import okhttp3.ResponseBody
-import retrofit2.Response
+import retrofit2.Call
 import java.io.IOException
 
-suspend inline fun <DATA> executeRequest(block: Request<DATA>.() -> Unit) = Request<DATA>().apply(block).execute()
+inline fun <DATA> executeRequest(block: Request<DATA>.() -> Unit) = Request<DATA>().apply(block).execute()
 
 class Request<DATA> {
 
     var moshi: Moshi = MoshiProvider.providesMoshi()
-    lateinit var apiCall: Deferred<Response<DATA>>
+    lateinit var apiCall: Call<DATA>
 
-    suspend fun execute(): Either<Failure, DATA?> = coroutineScope {
-        try {
-            val response = apiCall.await()
+    fun execute(): Try<DATA> {
+        return Try {
+            val response = apiCall.runAsync(IO.async()).fix().unsafeRunSync()
             if (response.isSuccessful) {
-                val result = response.body()
-                Either.Right(result)
+                response.body() ?: throw IllegalStateException("The request returned a null body")
             } else {
-                val failure = manageFailure(response.errorBody())
-                Either.Left(failure)
-            }
-
-        } catch (e: Exception) {
-            when (e) {
-                is IOException -> Either.Left(Failure.NetworkConnection(e))
-                else           -> Either.Left(Failure.Unknown(e))
+                throw manageFailure(response.errorBody())
             }
+        }.recoverWith {
+            when (it) {
+                is IOException         -> Failure.NetworkConnection(it)
+                is Failure.ServerError -> it
+                else                   -> Failure.Unknown(it)
+            }.failure()
         }
     }
 
-    private fun manageFailure(errorBody: ResponseBody?): Failure {
-        return try {
-            val matrixError = errorBody?.let {
-                val matrixErrorAdapter = moshi.adapter(MatrixError::class.java)
-                matrixErrorAdapter.fromJson(errorBody.source())
-            } ?: throw RuntimeException("Matrix error should not be null")
-
-            Failure.ServerError(matrixError)
-
-        } catch (e: Exception) {
-            Failure.Unknown(e)
-        }
+    private fun manageFailure(errorBody: ResponseBody?): Throwable {
+        val matrixError = errorBody?.let {
+            val matrixErrorAdapter = moshi.adapter(MatrixError::class.java)
+            matrixErrorAdapter.fromJson(errorBody.source())
+        } ?: return RuntimeException("Matrix error should not be null")
+        return Failure.ServerError(matrixError)
     }
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataRequest.kt
new file mode 100644
index 0000000000..8651b3096e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataRequest.kt
@@ -0,0 +1,70 @@
+package im.vector.matrix.android.internal.session.group
+
+import arrow.core.Try
+import arrow.core.fix
+import arrow.instances.`try`.monad.monad
+import arrow.typeclasses.binding
+import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.util.Cancelable
+import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
+import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.network.executeRequest
+import im.vector.matrix.android.internal.session.group.model.GroupRooms
+import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse
+import im.vector.matrix.android.internal.util.CancelableCoroutine
+import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
+import im.vector.matrix.android.internal.util.tryTransactionSync
+import io.realm.kotlin.createObject
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class GetGroupDataRequest(
+        private val groupAPI: GroupAPI,
+        private val monarchy: Monarchy,
+        private val coroutineDispatchers: MatrixCoroutineDispatchers
+) {
+
+    fun execute(groupId: String,
+                callback: MatrixCallback<Boolean>
+    ): Cancelable {
+        val job = GlobalScope.launch(coroutineDispatchers.main) {
+            val groupOrFailure = execute(groupId)
+            groupOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(true) })
+        }
+        return CancelableCoroutine(job)
+    }
+
+    private suspend fun execute(groupId: String) = withContext(coroutineDispatchers.io) {
+        Try.monad().binding {
+            val groupSummary = executeRequest<GroupSummaryResponse> {
+                apiCall = groupAPI.getSummary(groupId)
+            }.bind()
+
+            val groupRooms = executeRequest<GroupRooms> {
+                apiCall = groupAPI.getRooms(groupId)
+            }.bind()
+            insertInDb(groupSummary, groupRooms, groupId).bind()
+        }.fix()
+    }
+
+    private fun insertInDb(groupSummary: GroupSummaryResponse, groupRooms: GroupRooms, groupId: String): Try<Unit> {
+        return monarchy
+                .tryTransactionSync { realm ->
+                    val groupSummaryEntity = GroupSummaryEntity.where(realm, groupId).findFirst()
+                                             ?: realm.createObject(groupId)
+
+                    groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: ""
+                    val name = groupSummary.profile?.name
+                    groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name
+                    groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription ?: ""
+
+                    val roomIds = groupRooms.rooms.mapNotNull { it.roomId }
+                    groupSummaryEntity.roomIds.clear()
+                    groupSummaryEntity.roomIds.addAll(roomIds)
+                }
+    }
+
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt
deleted file mode 100644
index 4d73e73327..0000000000
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package im.vector.matrix.android.internal.session.group
-
-import arrow.core.Either
-import arrow.core.flatMap
-import arrow.core.leftIfNull
-import com.zhuinden.monarchy.Monarchy
-import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.failure.Failure
-import im.vector.matrix.android.api.util.Cancelable
-import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
-import im.vector.matrix.android.internal.database.query.where
-import im.vector.matrix.android.internal.network.executeRequest
-import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse
-import im.vector.matrix.android.internal.util.CancelableCoroutine
-import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
-import io.realm.kotlin.createObject
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class GetGroupSummaryRequest(
-        private val groupAPI: GroupAPI,
-        private val monarchy: Monarchy,
-        private val coroutineDispatchers: MatrixCoroutineDispatchers
-) {
-
-    fun execute(groupId: String,
-                callback: MatrixCallback<GroupSummaryResponse>
-    ): Cancelable {
-        val job = GlobalScope.launch(coroutineDispatchers.main) {
-            val groupOrFailure = execute(groupId)
-            groupOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
-        }
-        return CancelableCoroutine(job)
-    }
-
-    private suspend fun execute(groupId: String) = withContext(coroutineDispatchers.io) {
-
-        return@withContext executeRequest<GroupSummaryResponse> {
-            apiCall = groupAPI.getSummary(groupId)
-        }.leftIfNull {
-            Failure.Unknown(RuntimeException("GroupSummary shouldn't be null"))
-        }.flatMap { groupSummary ->
-            try {
-                insertInDb(groupSummary, groupId)
-                Either.right(groupSummary)
-            } catch (exception: Exception) {
-                Either.Left(Failure.Unknown(exception))
-            }
-        }
-    }
-
-    private fun insertInDb(groupSummary: GroupSummaryResponse, groupId: String) {
-        monarchy.runTransactionSync { realm ->
-            val groupSummaryEntity = GroupSummaryEntity.where(realm, groupId).findFirst()
-                                     ?: realm.createObject(groupId)
-
-            groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: ""
-            val name = groupSummary.profile?.name
-            groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name
-            groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription ?: ""
-
-
-        }
-    }
-
-
-}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt
index 1fe5bc4519..a3a6978fc9 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt
@@ -1,9 +1,9 @@
 package im.vector.matrix.android.internal.session.group
 
 import im.vector.matrix.android.internal.network.NetworkConstants
+import im.vector.matrix.android.internal.session.group.model.GroupRooms
 import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse
-import kotlinx.coroutines.Deferred
-import retrofit2.Response
+import retrofit2.Call
 import retrofit2.http.GET
 import retrofit2.http.Path
 
@@ -15,7 +15,15 @@ interface GroupAPI {
      * @param groupId the group id
      */
     @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "groups/{groupId}/summary")
-    fun getSummary(@Path("groupId") groupId: String): Deferred<Response<GroupSummaryResponse>>
+    fun getSummary(@Path("groupId") groupId: String): Call<GroupSummaryResponse>
+
+    /**
+     * Request the rooms list.
+     *
+     * @param groupId the group id
+     */
+    @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "groups/{groupId}/rooms")
+    fun getRooms(@Path("groupId") groupId: String): Call<GroupRooms>
 
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt
index 9334a2676c..1f8bf99ce3 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt
@@ -16,7 +16,7 @@ class GroupModule : Module {
         }
 
         scope(DefaultSession.SCOPE) {
-            GetGroupSummaryRequest(get(), get(), get())
+            GetGroupDataRequest(get(), get(), get())
         }
 
     }.invoke()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt
index 74b0bb5e20..8f256be4a7 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt
@@ -7,12 +7,11 @@ import im.vector.matrix.android.api.session.group.Group
 import im.vector.matrix.android.internal.database.mapper.asDomain
 import im.vector.matrix.android.internal.database.model.GroupEntity
 import im.vector.matrix.android.internal.database.query.where
-import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse
 import timber.log.Timber
 import java.util.concurrent.atomic.AtomicBoolean
 
 internal class GroupSummaryUpdater(private val monarchy: Monarchy,
-                                   private val getGroupSummaryRequest: GetGroupSummaryRequest
+                                   private val getGroupDataRequest: GetGroupDataRequest
 ) : Observer<Monarchy.ManagedChangeSet<GroupEntity>> {
 
     private var isStarted = AtomicBoolean(false)
@@ -57,7 +56,7 @@ internal class GroupSummaryUpdater(private val monarchy: Monarchy,
         if (group == null) {
             return
         }
-        getGroupSummaryRequest.execute(group.groupId, object : MatrixCallback<GroupSummaryResponse> {})
+        getGroupDataRequest.execute(group.groupId, object : MatrixCallback<Boolean> {})
     }
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRoom.kt
new file mode 100644
index 0000000000..fd3590b242
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRoom.kt
@@ -0,0 +1,19 @@
+package im.vector.matrix.android.internal.session.group.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class GroupRoom(
+
+        @Json(name = "aliases") val aliases: List<String>? = null,
+        @Json(name = "canonical_alias") val canonicalAlias: String? = null,
+        @Json(name = "name") val name: String? = null,
+        @Json(name = "num_joined_members") val numJoinedMembers: Int = 0,
+        @Json(name = "room_id") val roomId: String? = null,
+        @Json(name = "topic") val topic: String? = null,
+        @Json(name = "world_readable") val worldReadable: Boolean = false,
+        @Json(name = "guest_can_join") val guestCanJoin: Boolean = false,
+        @Json(name = "avatar_url") val avatarUrl: String? = null
+
+)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRooms.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRooms.kt
new file mode 100644
index 0000000000..b3b2ffdc61
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupRooms.kt
@@ -0,0 +1,12 @@
+package im.vector.matrix.android.internal.session.group.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class GroupRooms(
+
+        @Json(name = "total_room_count_estimate") val totalRoomCountEstimate: Int? = null,
+        @Json(name = "chunk") val rooms: List<GroupRoom> = emptyList()
+
+)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt
index 9909db718a..d2c3ca8659 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt
@@ -4,6 +4,7 @@ import im.vector.matrix.android.internal.network.NetworkConstants
 import im.vector.matrix.android.internal.session.room.members.RoomMembersResponse
 import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEvent
 import kotlinx.coroutines.Deferred
+import retrofit2.Call
 import retrofit2.Response
 import retrofit2.http.GET
 import retrofit2.http.Path
@@ -26,7 +27,7 @@ interface RoomAPI {
                             @Query("dir") dir: String,
                             @Query("limit") limit: Int,
                             @Query("filter") filter: String?
-    ): Deferred<Response<TokenChunkEvent>>
+    ): Call<TokenChunkEvent>
 
 
     /**
@@ -42,7 +43,7 @@ interface RoomAPI {
                    @Query("at") syncToken: String?,
                    @Query("membership") membership: String?,
                    @Query("not_membership") notMembership: String?
-    ): Deferred<Response<RoomMembersResponse>>
+    ): Call<RoomMembersResponse>
 
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt
index 0a84c25cb6..18147810f6 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt
@@ -1,11 +1,8 @@
 package im.vector.matrix.android.internal.session.room.members
 
-import arrow.core.Either
-import arrow.core.flatMap
-import arrow.core.leftIfNull
+import arrow.core.Try
 import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.internal.database.model.RoomEntity
@@ -15,6 +12,7 @@ import im.vector.matrix.android.internal.session.room.RoomAPI
 import im.vector.matrix.android.internal.session.sync.StateEventsChunkHandler
 import im.vector.matrix.android.internal.util.CancelableCoroutine
 import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
+import im.vector.matrix.android.internal.util.tryTransactionSync
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -31,7 +29,7 @@ internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI,
     ): Cancelable {
         val job = GlobalScope.launch(coroutineDispatchers.main) {
             val responseOrFailure = execute(roomId, streamToken, excludeMembership)
-            responseOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(true) })
+            responseOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(true) })
         }
         return CancelableCoroutine(job)
     }
@@ -41,35 +39,30 @@ internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI,
                                 streamToken: String?,
                                 excludeMembership: Membership?) = withContext(coroutineDispatchers.io) {
 
-        return@withContext executeRequest<RoomMembersResponse> {
+        executeRequest<RoomMembersResponse> {
             apiCall = roomAPI.getMembers(roomId, null, null, excludeMembership?.value)
-        }.leftIfNull {
-            Failure.Unknown(RuntimeException("RoomMembersResponse shouldn't be null"))
         }.flatMap { response ->
-            try {
-                insertInDb(response, roomId)
-                Either.right(response)
-            } catch (exception: Exception) {
-                Either.Left(Failure.Unknown(exception))
-            }
+            insertInDb(response, roomId)
         }
     }
 
-    private fun insertInDb(response: RoomMembersResponse, roomId: String) {
-        monarchy.runTransactionSync { realm ->
-            // We ignore all the already known members
-            val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                             ?: throw IllegalStateException("You shouldn't use this method without a room")
+    private fun insertInDb(response: RoomMembersResponse, roomId: String): Try<RoomMembersResponse> {
+        return monarchy
+                .tryTransactionSync { realm ->
+                    // We ignore all the already known members
+                    val roomEntity = RoomEntity.where(realm, roomId).findFirst()
+                                     ?: throw IllegalStateException("You shouldn't use this method without a room")
 
-            val roomMembers = RoomMembers(realm, roomId).getLoaded()
-            val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }
+                    val roomMembers = RoomMembers(realm, roomId).getLoaded()
+                    val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }
 
-            val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert)
-            if (!roomEntity.chunks.contains(chunk)) {
-                roomEntity.chunks.add(chunk)
-            }
-            roomEntity.areAllMembersLoaded = true
-        }
+                    val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert)
+                    if (!roomEntity.chunks.contains(chunk)) {
+                        roomEntity.chunks.add(chunk)
+                    }
+                    roomEntity.areAllMembersLoaded = true
+                }
+                .map { response }
     }
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt
index 2451a4ed38..742e93f540 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt
@@ -1,11 +1,9 @@
 package im.vector.matrix.android.internal.session.room.timeline
 
-import arrow.core.Either
-import arrow.core.flatMap
-import arrow.core.leftIfNull
+import arrow.core.Try
+import arrow.core.failure
 import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.internal.database.helper.addManagedToChunk
 import im.vector.matrix.android.internal.database.model.ChunkEntity
@@ -21,6 +19,7 @@ import im.vector.matrix.android.internal.session.room.RoomAPI
 import im.vector.matrix.android.internal.session.sync.StateEventsChunkHandler
 import im.vector.matrix.android.internal.util.CancelableCoroutine
 import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
+import im.vector.matrix.android.internal.util.tryTransactionSync
 import io.realm.kotlin.createObject
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
@@ -40,7 +39,7 @@ class PaginationRequest(private val roomAPI: RoomAPI,
         val job = GlobalScope.launch(coroutineDispatchers.main) {
             val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString()
             val chunkOrFailure = execute(roomId, from, direction, limit, filter)
-            chunkOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
+            chunkOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) })
         }
         return CancelableCoroutine(job)
     }
@@ -52,66 +51,59 @@ class PaginationRequest(private val roomAPI: RoomAPI,
                                 filter: String?) = withContext(coroutineDispatchers.io) {
 
         if (from == null) {
-            return@withContext Either.left(
-                    Failure.Unknown(RuntimeException("From token shouldn't be null"))
-            )
+            return@withContext RuntimeException("From token shouldn't be null").failure<TokenChunkEvent>()
         }
-        return@withContext executeRequest<TokenChunkEvent> {
+        executeRequest<TokenChunkEvent> {
             apiCall = roomAPI.getRoomMessagesFrom(roomId, from, direction.value, limit, filter)
-        }.leftIfNull {
-            Failure.Unknown(RuntimeException("TokenChunkEvent shouldn't be null"))
         }.flatMap { chunk ->
-            try {
-                insertInDb(chunk, roomId)
-                Either.right(chunk)
-            } catch (exception: Exception) {
-                Either.Left(Failure.Unknown(exception))
-            }
+            insertInDb(chunk, roomId)
         }
     }
 
-    private fun insertInDb(receivedChunk: TokenChunkEvent, roomId: String) {
-        monarchy.runTransactionSync { realm ->
-            val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                             ?: throw IllegalStateException("You shouldn't use this method without a room")
+    private fun insertInDb(receivedChunk: TokenChunkEvent, roomId: String): Try<TokenChunkEvent> {
+        return monarchy
+                .tryTransactionSync { realm ->
+                    val roomEntity = RoomEntity.where(realm, roomId).findFirst()
+                                     ?: throw IllegalStateException("You shouldn't use this method without a room")
 
-            val currentChunk = ChunkEntity.findWithPrevToken(realm, roomId, receivedChunk.nextToken)
-                               ?: realm.createObject()
+                    val currentChunk = ChunkEntity.findWithPrevToken(realm, roomId, receivedChunk.nextToken)
+                                       ?: realm.createObject()
 
-            currentChunk.prevToken = receivedChunk.prevToken
+                    currentChunk.prevToken = receivedChunk.prevToken
 
-            val prevChunk = ChunkEntity.findWithNextToken(realm, roomId, receivedChunk.prevToken)
+                    val prevChunk = ChunkEntity.findWithNextToken(realm, roomId, receivedChunk.prevToken)
 
-            val eventIds = receivedChunk.events.filter { it.eventId != null }.map { it.eventId!! }
-            val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, eventIds)
-            val hasOverlapped = chunksOverlapped.isNotEmpty()
+                    val eventIds = receivedChunk.events.filter { it.eventId != null }.map { it.eventId!! }
+                    val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, eventIds)
+                    val hasOverlapped = chunksOverlapped.isNotEmpty()
 
-            val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents)
-            if (!roomEntity.chunks.contains(stateEventsChunk)) {
-                roomEntity.chunks.add(stateEventsChunk)
-            }
+                    val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents)
+                    if (!roomEntity.chunks.contains(stateEventsChunk)) {
+                        roomEntity.chunks.add(stateEventsChunk)
+                    }
 
-            receivedChunk.events.addManagedToChunk(currentChunk)
+                    receivedChunk.events.addManagedToChunk(currentChunk)
 
-            if (prevChunk != null) {
-                currentChunk.events.addAll(prevChunk.events)
-                roomEntity.chunks.remove(prevChunk)
+                    if (prevChunk != null) {
+                        currentChunk.events.addAll(prevChunk.events)
+                        roomEntity.chunks.remove(prevChunk)
 
-            } else if (hasOverlapped) {
-                chunksOverlapped.forEach { overlapped ->
-                    overlapped.events.forEach { event ->
-                        if (!currentChunk.events.fastContains(event)) {
-                            currentChunk.events.add(event)
+                    } else if (hasOverlapped) {
+                        chunksOverlapped.forEach { overlapped ->
+                            overlapped.events.forEach { event ->
+                                if (!currentChunk.events.fastContains(event)) {
+                                    currentChunk.events.add(event)
+                                }
+                            }
+                            currentChunk.prevToken = overlapped.prevToken
+                            roomEntity.chunks.remove(overlapped)
                         }
                     }
-                    currentChunk.prevToken = overlapped.prevToken
-                    roomEntity.chunks.remove(overlapped)
+                    if (!roomEntity.chunks.contains(currentChunk)) {
+                        roomEntity.chunks.add(currentChunk)
+                    }
                 }
-            }
-            if (!roomEntity.chunks.contains(currentChunk)) {
-                roomEntity.chunks.add(currentChunk)
-            }
-        }
+                .map { receivedChunk }
     }
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt
index c8d76d7da2..2f94322033 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt
@@ -3,7 +3,6 @@ package im.vector.matrix.android.internal.session.room.timeline
 import android.arch.paging.PagedList
 import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.session.events.model.EnrichedEvent
 import im.vector.matrix.android.internal.database.model.ChunkEntity
 import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
@@ -54,7 +53,7 @@ class TimelineBoundaryCallback(private val roomId: String,
             pagingRequestCallback.recordSuccess()
         }
 
-        override fun onFailure(failure: Failure) {
+        override fun onFailure(failure: Throwable) {
             pagingRequestCallback.recordFailure(failure)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncAPI.kt
index 6652648942..b5aa09815c 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncAPI.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncAPI.kt
@@ -1,15 +1,14 @@
 package im.vector.matrix.android.internal.session.sync
 
-import im.vector.matrix.android.internal.session.sync.model.SyncResponse
 import im.vector.matrix.android.internal.network.NetworkConstants
-import kotlinx.coroutines.Deferred
-import retrofit2.Response
+import im.vector.matrix.android.internal.session.sync.model.SyncResponse
+import retrofit2.Call
 import retrofit2.http.GET
 import retrofit2.http.QueryMap
 
 interface SyncAPI {
 
     @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sync")
-    fun sync(@QueryMap params: Map<String, String>): Deferred<Response<SyncResponse>>
+    fun sync(@QueryMap params: Map<String, String>): Call<SyncResponse>
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncRequest.kt
index db5aafb146..6f4c5d3ecf 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncRequest.kt
@@ -1,10 +1,6 @@
 package im.vector.matrix.android.internal.session.sync
 
-import arrow.core.Either
-import arrow.core.flatMap
-import arrow.core.leftIfNull
 import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.internal.legacy.rest.model.filter.FilterBody
 import im.vector.matrix.android.internal.legacy.util.FilterUtil
@@ -24,7 +20,7 @@ internal class SyncRequest(private val syncAPI: SyncAPI,
     fun execute(token: String?, callback: MatrixCallback<SyncResponse>): Cancelable {
         val job = GlobalScope.launch {
             val syncOrFailure = execute(token)
-            syncOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
+            syncOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) })
         }
         return CancelableCoroutine(job)
     }
@@ -42,15 +38,8 @@ internal class SyncRequest(private val syncAPI: SyncAPI,
         params["filter"] = filterBody.toJSONString()
         executeRequest<SyncResponse> {
             apiCall = syncAPI.sync(params)
-        }.leftIfNull {
-            Failure.Unknown(RuntimeException("Sync response shouln't be null"))
-        }.flatMap {
-            try {
-                syncResponseHandler.handleResponse(it, token, false)
-                Either.right(it)
-            } catch (exception: Exception) {
-                Either.Left(Failure.Unknown(exception))
-            }
+        }.flatMap { syncResponse ->
+            syncResponseHandler.handleResponse(syncResponse, token, false)
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt
index a41ec41f20..e427d2971c 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt
@@ -1,5 +1,6 @@
 package im.vector.matrix.android.internal.session.sync
 
+import arrow.core.Try
 import im.vector.matrix.android.internal.session.sync.model.SyncResponse
 import timber.log.Timber
 
@@ -7,21 +8,20 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
                                    private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
                                    private val groupSyncHandler: GroupSyncHandler) {
 
-    fun handleResponse(syncResponse: SyncResponse?, fromToken: String?, isCatchingUp: Boolean) {
-        if (syncResponse == null) {
-            return
-        }
-        Timber.v("Handle sync response")
-        if (syncResponse.rooms != null) {
-            roomSyncHandler.handle(syncResponse.rooms)
-        }
-        if (syncResponse.groups != null) {
-            groupSyncHandler.handle(syncResponse.groups)
-        }
-        if (syncResponse.accountData != null) {
-            userAccountDataSyncHandler.handle(syncResponse.accountData)
+    fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
+        return Try {
+            Timber.v("Handle sync response")
+            if (syncResponse.rooms != null) {
+                roomSyncHandler.handle(syncResponse.rooms)
+            }
+            if (syncResponse.groups != null) {
+                groupSyncHandler.handle(syncResponse.groups)
+            }
+            if (syncResponse.accountData != null) {
+                userAccountDataSyncHandler.handle(syncResponse.accountData)
+            }
+            syncResponse
         }
     }
 
-
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt
index dc11202e47..67617bfba2 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt
@@ -84,7 +84,7 @@ internal class SyncThread(private val syncRequest: SyncRequest,
                         latch.countDown()
                     }
 
-                    override fun onFailure(failure: Failure) {
+                    override fun onFailure(failure: Throwable) {
                         if (failure !is Failure.NetworkConnection) {
                             // Wait 10s before retrying
                             sleep(RETRY_WAIT_TIME_MS)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt
new file mode 100644
index 0000000000..5f80a08302
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt
@@ -0,0 +1,17 @@
+package im.vector.matrix.android.internal.util
+
+import arrow.core.Try
+import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+
+fun Monarchy.tryTransactionSync(transaction: (realm: Realm) -> Unit): Try<Unit> {
+    return Try {
+        this.runTransactionSync(transaction)
+    }
+}
+
+fun Monarchy.tryTransactionAsync(transaction: (realm: Realm) -> Unit): Try<Unit> {
+    return Try {
+        this.writeAsync(transaction)
+    }
+}
\ No newline at end of file