mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 03:48:12 +03:00
Use Try instead of Either as it makes more sens + add GroupRooms API
This commit is contained in:
parent
56bbcf209f
commit
9cc3dc51cc
30 changed files with 276 additions and 254 deletions
Binary file not shown.
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
)
|
|
@ -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>
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ object GroupSummaryMapper {
|
|||
roomSummaryEntity.groupId,
|
||||
roomSummaryEntity.displayName,
|
||||
roomSummaryEntity.shortDescription,
|
||||
roomSummaryEntity.avatarUrl
|
||||
roomSummaryEntity.avatarUrl,
|
||||
roomSummaryEntity.roomIds.toList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 ?: ""
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
||||
}
|
|
@ -16,7 +16,7 @@ class GroupModule : Module {
|
|||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
GetGroupSummaryRequest(get(), get(), get())
|
||||
GetGroupDataRequest(get(), get(), get())
|
||||
}
|
||||
|
||||
}.invoke()
|
||||
|
|
|
@ -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> {})
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
)
|
|
@ -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()
|
||||
|
||||
)
|
|
@ -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>
|
||||
|
||||
|
||||
}
|
|
@ -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 }
|
||||
}
|
||||
|
||||
}
|
|
@ -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 }
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue