mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-04 21:46:15 +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
.idea/caches
app/src/main/java/im/vector/riotredesign/features/login
matrix-sdk-android
build.gradle
src/main/java/im/vector/matrix/android
api
internal
auth
database
di
network
session
group
room
sync
util
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
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.Matrix
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
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.session.Session
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.platform.RiotActivity
|
import im.vector.riotredesign.core.platform.RiotActivity
|
||||||
|
@ -43,7 +42,7 @@ class LoginActivity : RiotActivity() {
|
||||||
goToHome()
|
goToHome()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Failure) {
|
override fun onFailure(failure: Throwable) {
|
||||||
progressBar.visibility = View.GONE
|
progressBar.visibility = View.GONE
|
||||||
Toast.makeText(this@LoginActivity, "Authenticate failure: $failure", Toast.LENGTH_LONG).show()
|
Toast.makeText(this@LoginActivity, "Authenticate failure: $failure", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,13 +41,14 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
def arrow_version = "0.7.3"
|
def arrow_version = "0.8.0"
|
||||||
def support_version = '28.0.0'
|
def support_version = '28.0.0'
|
||||||
def moshi_version = '1.7.0'
|
def moshi_version = '1.7.0'
|
||||||
def lifecycle_version = "1.1.1"
|
def lifecycle_version = "1.1.1"
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
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:appcompat-v7:$support_version"
|
||||||
implementation "com.android.support:recyclerview-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:retrofit:2.4.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson: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:okhttp:3.10.0'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
|
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
|
||||||
implementation 'com.squareup.okio:okio:1.15.0'
|
implementation 'com.squareup.okio:okio:1.15.0'
|
||||||
|
@ -77,6 +77,10 @@ dependencies {
|
||||||
|
|
||||||
// FP
|
// FP
|
||||||
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
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
|
// DI
|
||||||
implementation "org.koin:koin-core:$koin_version"
|
implementation "org.koin:koin-core:$koin_version"
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package im.vector.matrix.android.api
|
package im.vector.matrix.android.api
|
||||||
|
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
|
||||||
|
|
||||||
interface MatrixCallback<in T> {
|
interface MatrixCallback<in T> {
|
||||||
|
|
||||||
fun onSuccess(data: T) {
|
fun onSuccess(data: T) {
|
||||||
//no-op
|
//no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onFailure(failure: Failure){
|
fun onFailure(failure: Throwable) {
|
||||||
//no-op
|
//no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,6 @@ data class GroupSummary(
|
||||||
val groupId: String,
|
val groupId: String,
|
||||||
val displayName: String = "",
|
val displayName: String = "",
|
||||||
val shortDescription: 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.Credentials
|
||||||
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import kotlinx.coroutines.Deferred
|
import retrofit2.Call
|
||||||
import retrofit2.Response
|
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
|
|
||||||
|
@ -19,6 +18,6 @@ interface AuthAPI {
|
||||||
* @param loginParams the login parameters
|
* @param loginParams the login parameters
|
||||||
*/
|
*/
|
||||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
|
@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
|
package im.vector.matrix.android.internal.auth
|
||||||
|
|
||||||
import android.util.Patterns
|
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.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.Authenticator
|
import im.vector.matrix.android.api.auth.Authenticator
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
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.session.Session
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.auth.data.Credentials
|
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 job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password)
|
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)
|
return CancelableCoroutine(job)
|
||||||
|
|
||||||
|
@ -52,7 +49,7 @@ class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
|
||||||
|
|
||||||
private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
|
private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||||
login: String,
|
login: String,
|
||||||
password: String): Either<Failure, Session> = withContext(coroutineDispatchers.io) {
|
password: String) = withContext(coroutineDispatchers.io) {
|
||||||
|
|
||||||
val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||||
val loginParams = if (Patterns.EMAIL_ADDRESS.matcher(login).matches()) {
|
val loginParams = if (Patterns.EMAIL_ADDRESS.matcher(login).matches()) {
|
||||||
|
@ -62,8 +59,6 @@ class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
|
||||||
}
|
}
|
||||||
executeRequest<Credentials> {
|
executeRequest<Credentials> {
|
||||||
apiCall = authAPI.login(loginParams)
|
apiCall = authAPI.login(loginParams)
|
||||||
}.leftIfNull {
|
|
||||||
Failure.Unknown(IllegalArgumentException("Credentials shouldn't not be null"))
|
|
||||||
}.map {
|
}.map {
|
||||||
val sessionParams = SessionParams(it, homeServerConnectionConfig)
|
val sessionParams = SessionParams(it, homeServerConnectionConfig)
|
||||||
sessionParamsStore.save(sessionParams)
|
sessionParamsStore.save(sessionParams)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package im.vector.matrix.android.internal.auth
|
package im.vector.matrix.android.internal.auth
|
||||||
|
|
||||||
|
import arrow.core.Try
|
||||||
import im.vector.matrix.android.internal.auth.data.SessionParams
|
import im.vector.matrix.android.internal.auth.data.SessionParams
|
||||||
|
|
||||||
interface SessionParamsStore {
|
interface SessionParamsStore {
|
||||||
|
|
||||||
fun get(): SessionParams?
|
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
|
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.SessionParamsStore
|
||||||
import im.vector.matrix.android.internal.auth.data.SessionParams
|
import im.vector.matrix.android.internal.auth.data.SessionParams
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
@ -8,14 +9,17 @@ import io.realm.RealmConfiguration
|
||||||
class RealmSessionParamsStore(private val mapper: SessionParamsMapper,
|
class RealmSessionParamsStore(private val mapper: SessionParamsMapper,
|
||||||
private val realmConfiguration: RealmConfiguration) : SessionParamsStore {
|
private val realmConfiguration: RealmConfiguration) : SessionParamsStore {
|
||||||
|
|
||||||
override fun save(sessionParams: SessionParams) {
|
override fun save(sessionParams: SessionParams): Try<SessionParams> {
|
||||||
val entity = mapper.map(sessionParams)
|
return Try {
|
||||||
if (entity != null) {
|
val entity = mapper.map(sessionParams)
|
||||||
val realm = Realm.getInstance(realmConfiguration)
|
if (entity != null) {
|
||||||
realm.executeTransaction {
|
val realm = Realm.getInstance(realmConfiguration)
|
||||||
it.insert(entity)
|
realm.executeTransaction {
|
||||||
|
it.insert(entity)
|
||||||
|
}
|
||||||
|
realm.close()
|
||||||
}
|
}
|
||||||
realm.close()
|
sessionParams
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ object GroupSummaryMapper {
|
||||||
roomSummaryEntity.groupId,
|
roomSummaryEntity.groupId,
|
||||||
roomSummaryEntity.displayName,
|
roomSummaryEntity.displayName,
|
||||||
roomSummaryEntity.shortDescription,
|
roomSummaryEntity.shortDescription,
|
||||||
roomSummaryEntity.avatarUrl
|
roomSummaryEntity.avatarUrl,
|
||||||
|
roomSummaryEntity.roomIds.toList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package im.vector.matrix.android.internal.database.model
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
|
import io.realm.RealmList
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
|
|
||||||
open class GroupSummaryEntity(@PrimaryKey var groupId: String = "",
|
open class GroupSummaryEntity(@PrimaryKey var groupId: String = "",
|
||||||
var displayName: String = "",
|
var displayName: String = "",
|
||||||
var shortDescription: String = "",
|
var shortDescription: String = "",
|
||||||
var avatarUrl: String = ""
|
var avatarUrl: String = "",
|
||||||
|
var roomIds: RealmList<String> = RealmList()
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
companion object
|
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.BackgroundDetectionObserver
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.IO
|
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import org.koin.dsl.context.ModuleDefinition
|
import org.koin.dsl.context.ModuleDefinition
|
||||||
import org.koin.dsl.module.Module
|
import org.koin.dsl.module.Module
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package im.vector.matrix.android.internal.di
|
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.AccessTokenInterceptor
|
||||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
@ -8,7 +7,6 @@ import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import org.koin.dsl.context.ModuleDefinition
|
import org.koin.dsl.context.ModuleDefinition
|
||||||
import org.koin.dsl.module.Module
|
import org.koin.dsl.module.Module
|
||||||
import org.koin.dsl.module.module
|
import org.koin.dsl.module.module
|
||||||
import retrofit2.CallAdapter
|
|
||||||
import retrofit2.Converter
|
import retrofit2.Converter
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
|
@ -48,10 +46,6 @@ class NetworkModule : Module {
|
||||||
MoshiConverterFactory.create(get()) as Converter.Factory
|
MoshiConverterFactory.create(get()) as Converter.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
single {
|
|
||||||
CoroutineCallAdapterFactory() as CallAdapter.Factory
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
single {
|
||||||
NetworkConnectivityChecker(get())
|
NetworkConnectivityChecker(get())
|
||||||
}
|
}
|
||||||
|
@ -60,7 +54,6 @@ class NetworkModule : Module {
|
||||||
Retrofit.Builder()
|
Retrofit.Builder()
|
||||||
.client(get())
|
.client(get())
|
||||||
.addConverterFactory(get())
|
.addConverterFactory(get())
|
||||||
.addCallAdapterFactory(get())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}.invoke()
|
}.invoke()
|
||||||
|
|
|
@ -1,54 +1,50 @@
|
||||||
package im.vector.matrix.android.internal.network
|
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 com.squareup.moshi.Moshi
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.failure.MatrixError
|
import im.vector.matrix.android.api.failure.MatrixError
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import kotlinx.coroutines.Deferred
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import retrofit2.Response
|
import retrofit2.Call
|
||||||
import java.io.IOException
|
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> {
|
class Request<DATA> {
|
||||||
|
|
||||||
var moshi: Moshi = MoshiProvider.providesMoshi()
|
var moshi: Moshi = MoshiProvider.providesMoshi()
|
||||||
lateinit var apiCall: Deferred<Response<DATA>>
|
lateinit var apiCall: Call<DATA>
|
||||||
|
|
||||||
suspend fun execute(): Either<Failure, DATA?> = coroutineScope {
|
fun execute(): Try<DATA> {
|
||||||
try {
|
return Try {
|
||||||
val response = apiCall.await()
|
val response = apiCall.runAsync(IO.async()).fix().unsafeRunSync()
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val result = response.body()
|
response.body() ?: throw IllegalStateException("The request returned a null body")
|
||||||
Either.Right(result)
|
|
||||||
} else {
|
} else {
|
||||||
val failure = manageFailure(response.errorBody())
|
throw manageFailure(response.errorBody())
|
||||||
Either.Left(failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
when (e) {
|
|
||||||
is IOException -> Either.Left(Failure.NetworkConnection(e))
|
|
||||||
else -> Either.Left(Failure.Unknown(e))
|
|
||||||
}
|
}
|
||||||
|
}.recoverWith {
|
||||||
|
when (it) {
|
||||||
|
is IOException -> Failure.NetworkConnection(it)
|
||||||
|
is Failure.ServerError -> it
|
||||||
|
else -> Failure.Unknown(it)
|
||||||
|
}.failure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun manageFailure(errorBody: ResponseBody?): Failure {
|
private fun manageFailure(errorBody: ResponseBody?): Throwable {
|
||||||
return try {
|
val matrixError = errorBody?.let {
|
||||||
val matrixError = errorBody?.let {
|
val matrixErrorAdapter = moshi.adapter(MatrixError::class.java)
|
||||||
val matrixErrorAdapter = moshi.adapter(MatrixError::class.java)
|
matrixErrorAdapter.fromJson(errorBody.source())
|
||||||
matrixErrorAdapter.fromJson(errorBody.source())
|
} ?: return RuntimeException("Matrix error should not be null")
|
||||||
} ?: throw RuntimeException("Matrix error should not be null")
|
return Failure.ServerError(matrixError)
|
||||||
|
|
||||||
Failure.ServerError(matrixError)
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Failure.Unknown(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
package im.vector.matrix.android.internal.session.group
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
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 im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse
|
||||||
import kotlinx.coroutines.Deferred
|
import retrofit2.Call
|
||||||
import retrofit2.Response
|
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
|
|
||||||
|
@ -15,7 +15,15 @@ interface GroupAPI {
|
||||||
* @param groupId the group id
|
* @param groupId the group id
|
||||||
*/
|
*/
|
||||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "groups/{groupId}/summary")
|
@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) {
|
scope(DefaultSession.SCOPE) {
|
||||||
GetGroupSummaryRequest(get(), get(), get())
|
GetGroupDataRequest(get(), get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
}.invoke()
|
}.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.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.GroupEntity
|
import im.vector.matrix.android.internal.database.model.GroupEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
internal class GroupSummaryUpdater(private val monarchy: Monarchy,
|
internal class GroupSummaryUpdater(private val monarchy: Monarchy,
|
||||||
private val getGroupSummaryRequest: GetGroupSummaryRequest
|
private val getGroupDataRequest: GetGroupDataRequest
|
||||||
) : Observer<Monarchy.ManagedChangeSet<GroupEntity>> {
|
) : Observer<Monarchy.ManagedChangeSet<GroupEntity>> {
|
||||||
|
|
||||||
private var isStarted = AtomicBoolean(false)
|
private var isStarted = AtomicBoolean(false)
|
||||||
|
@ -57,7 +56,7 @@ internal class GroupSummaryUpdater(private val monarchy: Monarchy,
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
return
|
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.members.RoomMembersResponse
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEvent
|
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEvent
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
|
import retrofit2.Call
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
|
@ -26,7 +27,7 @@ interface RoomAPI {
|
||||||
@Query("dir") dir: String,
|
@Query("dir") dir: String,
|
||||||
@Query("limit") limit: Int,
|
@Query("limit") limit: Int,
|
||||||
@Query("filter") filter: String?
|
@Query("filter") filter: String?
|
||||||
): Deferred<Response<TokenChunkEvent>>
|
): Call<TokenChunkEvent>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +43,7 @@ interface RoomAPI {
|
||||||
@Query("at") syncToken: String?,
|
@Query("at") syncToken: String?,
|
||||||
@Query("membership") membership: String?,
|
@Query("membership") membership: String?,
|
||||||
@Query("not_membership") notMembership: String?
|
@Query("not_membership") notMembership: String?
|
||||||
): Deferred<Response<RoomMembersResponse>>
|
): Call<RoomMembersResponse>
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
package im.vector.matrix.android.internal.session.room.members
|
package im.vector.matrix.android.internal.session.room.members
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Try
|
||||||
import arrow.core.flatMap
|
|
||||||
import arrow.core.leftIfNull
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
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.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
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.session.sync.StateEventsChunkHandler
|
||||||
import im.vector.matrix.android.internal.util.CancelableCoroutine
|
import im.vector.matrix.android.internal.util.CancelableCoroutine
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
|
import im.vector.matrix.android.internal.util.tryTransactionSync
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -31,7 +29,7 @@ internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI,
|
||||||
): Cancelable {
|
): Cancelable {
|
||||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
val responseOrFailure = execute(roomId, streamToken, excludeMembership)
|
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)
|
return CancelableCoroutine(job)
|
||||||
}
|
}
|
||||||
|
@ -41,35 +39,30 @@ internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI,
|
||||||
streamToken: String?,
|
streamToken: String?,
|
||||||
excludeMembership: Membership?) = withContext(coroutineDispatchers.io) {
|
excludeMembership: Membership?) = withContext(coroutineDispatchers.io) {
|
||||||
|
|
||||||
return@withContext executeRequest<RoomMembersResponse> {
|
executeRequest<RoomMembersResponse> {
|
||||||
apiCall = roomAPI.getMembers(roomId, null, null, excludeMembership?.value)
|
apiCall = roomAPI.getMembers(roomId, null, null, excludeMembership?.value)
|
||||||
}.leftIfNull {
|
|
||||||
Failure.Unknown(RuntimeException("RoomMembersResponse shouldn't be null"))
|
|
||||||
}.flatMap { response ->
|
}.flatMap { response ->
|
||||||
try {
|
insertInDb(response, roomId)
|
||||||
insertInDb(response, roomId)
|
|
||||||
Either.right(response)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Either.Left(Failure.Unknown(exception))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun insertInDb(response: RoomMembersResponse, roomId: String) {
|
private fun insertInDb(response: RoomMembersResponse, roomId: String): Try<RoomMembersResponse> {
|
||||||
monarchy.runTransactionSync { realm ->
|
return monarchy
|
||||||
// We ignore all the already known members
|
.tryTransactionSync { realm ->
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
// We ignore all the already known members
|
||||||
?: throw IllegalStateException("You shouldn't use this method without a room")
|
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 roomMembers = RoomMembers(realm, roomId).getLoaded()
|
||||||
val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }
|
val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }
|
||||||
|
|
||||||
val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert)
|
val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert)
|
||||||
if (!roomEntity.chunks.contains(chunk)) {
|
if (!roomEntity.chunks.contains(chunk)) {
|
||||||
roomEntity.chunks.add(chunk)
|
roomEntity.chunks.add(chunk)
|
||||||
}
|
}
|
||||||
roomEntity.areAllMembersLoaded = true
|
roomEntity.areAllMembersLoaded = true
|
||||||
}
|
}
|
||||||
|
.map { response }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,9 @@
|
||||||
package im.vector.matrix.android.internal.session.room.timeline
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Try
|
||||||
import arrow.core.flatMap
|
import arrow.core.failure
|
||||||
import arrow.core.leftIfNull
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
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.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.database.helper.addManagedToChunk
|
import im.vector.matrix.android.internal.database.helper.addManagedToChunk
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
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.session.sync.StateEventsChunkHandler
|
||||||
import im.vector.matrix.android.internal.util.CancelableCoroutine
|
import im.vector.matrix.android.internal.util.CancelableCoroutine
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
|
import im.vector.matrix.android.internal.util.tryTransactionSync
|
||||||
import io.realm.kotlin.createObject
|
import io.realm.kotlin.createObject
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -40,7 +39,7 @@ class PaginationRequest(private val roomAPI: RoomAPI,
|
||||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString()
|
val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString()
|
||||||
val chunkOrFailure = execute(roomId, from, direction, limit, filter)
|
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)
|
return CancelableCoroutine(job)
|
||||||
}
|
}
|
||||||
|
@ -52,66 +51,59 @@ class PaginationRequest(private val roomAPI: RoomAPI,
|
||||||
filter: String?) = withContext(coroutineDispatchers.io) {
|
filter: String?) = withContext(coroutineDispatchers.io) {
|
||||||
|
|
||||||
if (from == null) {
|
if (from == null) {
|
||||||
return@withContext Either.left(
|
return@withContext RuntimeException("From token shouldn't be null").failure<TokenChunkEvent>()
|
||||||
Failure.Unknown(RuntimeException("From token shouldn't be null"))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return@withContext executeRequest<TokenChunkEvent> {
|
executeRequest<TokenChunkEvent> {
|
||||||
apiCall = roomAPI.getRoomMessagesFrom(roomId, from, direction.value, limit, filter)
|
apiCall = roomAPI.getRoomMessagesFrom(roomId, from, direction.value, limit, filter)
|
||||||
}.leftIfNull {
|
|
||||||
Failure.Unknown(RuntimeException("TokenChunkEvent shouldn't be null"))
|
|
||||||
}.flatMap { chunk ->
|
}.flatMap { chunk ->
|
||||||
try {
|
insertInDb(chunk, roomId)
|
||||||
insertInDb(chunk, roomId)
|
|
||||||
Either.right(chunk)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Either.Left(Failure.Unknown(exception))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun insertInDb(receivedChunk: TokenChunkEvent, roomId: String) {
|
private fun insertInDb(receivedChunk: TokenChunkEvent, roomId: String): Try<TokenChunkEvent> {
|
||||||
monarchy.runTransactionSync { realm ->
|
return monarchy
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
.tryTransactionSync { realm ->
|
||||||
?: throw IllegalStateException("You shouldn't use this method without a room")
|
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)
|
val currentChunk = ChunkEntity.findWithPrevToken(realm, roomId, receivedChunk.nextToken)
|
||||||
?: realm.createObject()
|
?: 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 eventIds = receivedChunk.events.filter { it.eventId != null }.map { it.eventId!! }
|
||||||
val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, eventIds)
|
val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, eventIds)
|
||||||
val hasOverlapped = chunksOverlapped.isNotEmpty()
|
val hasOverlapped = chunksOverlapped.isNotEmpty()
|
||||||
|
|
||||||
val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents)
|
val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents)
|
||||||
if (!roomEntity.chunks.contains(stateEventsChunk)) {
|
if (!roomEntity.chunks.contains(stateEventsChunk)) {
|
||||||
roomEntity.chunks.add(stateEventsChunk)
|
roomEntity.chunks.add(stateEventsChunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
receivedChunk.events.addManagedToChunk(currentChunk)
|
receivedChunk.events.addManagedToChunk(currentChunk)
|
||||||
|
|
||||||
if (prevChunk != null) {
|
if (prevChunk != null) {
|
||||||
currentChunk.events.addAll(prevChunk.events)
|
currentChunk.events.addAll(prevChunk.events)
|
||||||
roomEntity.chunks.remove(prevChunk)
|
roomEntity.chunks.remove(prevChunk)
|
||||||
|
|
||||||
} else if (hasOverlapped) {
|
} else if (hasOverlapped) {
|
||||||
chunksOverlapped.forEach { overlapped ->
|
chunksOverlapped.forEach { overlapped ->
|
||||||
overlapped.events.forEach { event ->
|
overlapped.events.forEach { event ->
|
||||||
if (!currentChunk.events.fastContains(event)) {
|
if (!currentChunk.events.fastContains(event)) {
|
||||||
currentChunk.events.add(event)
|
currentChunk.events.add(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentChunk.prevToken = overlapped.prevToken
|
||||||
|
roomEntity.chunks.remove(overlapped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentChunk.prevToken = overlapped.prevToken
|
if (!roomEntity.chunks.contains(currentChunk)) {
|
||||||
roomEntity.chunks.remove(overlapped)
|
roomEntity.chunks.add(currentChunk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.map { receivedChunk }
|
||||||
if (!roomEntity.chunks.contains(currentChunk)) {
|
|
||||||
roomEntity.chunks.add(currentChunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,7 +3,6 @@ package im.vector.matrix.android.internal.session.room.timeline
|
||||||
import android.arch.paging.PagedList
|
import android.arch.paging.PagedList
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
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.api.session.events.model.EnrichedEvent
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
|
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
|
||||||
|
@ -54,7 +53,7 @@ class TimelineBoundaryCallback(private val roomId: String,
|
||||||
pagingRequestCallback.recordSuccess()
|
pagingRequestCallback.recordSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Failure) {
|
override fun onFailure(failure: Throwable) {
|
||||||
pagingRequestCallback.recordFailure(failure)
|
pagingRequestCallback.recordFailure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
package im.vector.matrix.android.internal.session.sync
|
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 im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import kotlinx.coroutines.Deferred
|
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||||
import retrofit2.Response
|
import retrofit2.Call
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.QueryMap
|
import retrofit2.http.QueryMap
|
||||||
|
|
||||||
interface SyncAPI {
|
interface SyncAPI {
|
||||||
|
|
||||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sync")
|
@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
|
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.MatrixCallback
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
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.rest.model.filter.FilterBody
|
||||||
import im.vector.matrix.android.internal.legacy.util.FilterUtil
|
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 {
|
fun execute(token: String?, callback: MatrixCallback<SyncResponse>): Cancelable {
|
||||||
val job = GlobalScope.launch {
|
val job = GlobalScope.launch {
|
||||||
val syncOrFailure = execute(token)
|
val syncOrFailure = execute(token)
|
||||||
syncOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
|
syncOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) })
|
||||||
}
|
}
|
||||||
return CancelableCoroutine(job)
|
return CancelableCoroutine(job)
|
||||||
}
|
}
|
||||||
|
@ -42,15 +38,8 @@ internal class SyncRequest(private val syncAPI: SyncAPI,
|
||||||
params["filter"] = filterBody.toJSONString()
|
params["filter"] = filterBody.toJSONString()
|
||||||
executeRequest<SyncResponse> {
|
executeRequest<SyncResponse> {
|
||||||
apiCall = syncAPI.sync(params)
|
apiCall = syncAPI.sync(params)
|
||||||
}.leftIfNull {
|
}.flatMap { syncResponse ->
|
||||||
Failure.Unknown(RuntimeException("Sync response shouln't be null"))
|
syncResponseHandler.handleResponse(syncResponse, token, false)
|
||||||
}.flatMap {
|
|
||||||
try {
|
|
||||||
syncResponseHandler.handleResponse(it, token, false)
|
|
||||||
Either.right(it)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Either.Left(Failure.Unknown(exception))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package im.vector.matrix.android.internal.session.sync
|
package im.vector.matrix.android.internal.session.sync
|
||||||
|
|
||||||
|
import arrow.core.Try
|
||||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -7,21 +8,20 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
|
||||||
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
||||||
private val groupSyncHandler: GroupSyncHandler) {
|
private val groupSyncHandler: GroupSyncHandler) {
|
||||||
|
|
||||||
fun handleResponse(syncResponse: SyncResponse?, fromToken: String?, isCatchingUp: Boolean) {
|
fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
|
||||||
if (syncResponse == null) {
|
return Try {
|
||||||
return
|
Timber.v("Handle sync response")
|
||||||
}
|
if (syncResponse.rooms != null) {
|
||||||
Timber.v("Handle sync response")
|
roomSyncHandler.handle(syncResponse.rooms)
|
||||||
if (syncResponse.rooms != null) {
|
}
|
||||||
roomSyncHandler.handle(syncResponse.rooms)
|
if (syncResponse.groups != null) {
|
||||||
}
|
groupSyncHandler.handle(syncResponse.groups)
|
||||||
if (syncResponse.groups != null) {
|
}
|
||||||
groupSyncHandler.handle(syncResponse.groups)
|
if (syncResponse.accountData != null) {
|
||||||
}
|
userAccountDataSyncHandler.handle(syncResponse.accountData)
|
||||||
if (syncResponse.accountData != null) {
|
}
|
||||||
userAccountDataSyncHandler.handle(syncResponse.accountData)
|
syncResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -84,7 +84,7 @@ internal class SyncThread(private val syncRequest: SyncRequest,
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Failure) {
|
override fun onFailure(failure: Throwable) {
|
||||||
if (failure !is Failure.NetworkConnection) {
|
if (failure !is Failure.NetworkConnection) {
|
||||||
// Wait 10s before retrying
|
// Wait 10s before retrying
|
||||||
sleep(RETRY_WAIT_TIME_MS)
|
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…
Add table
Reference in a new issue