mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-29 14:38:45 +03:00
Room upgrade: add rx flux and handle failures more precisely
This commit is contained in:
parent
f4df27c2dc
commit
dc4786ecf0
14 changed files with 118 additions and 45 deletions
|
@ -45,6 +45,10 @@ class RxRoom(private val room: Room) {
|
||||||
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
|
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun joinRoom(viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
|
||||||
|
room.join(viaServers, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Room.rx(): RxRoom {
|
fun Room.rx(): RxRoom {
|
||||||
|
|
|
@ -63,6 +63,10 @@ class RxSession(private val session: Session) {
|
||||||
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(it)
|
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun joinRoom(roomId: String, viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
|
||||||
|
session.joinRoom(roomId, viaServers, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Session.rx(): RxSession {
|
fun Session.rx(): RxSession {
|
||||||
|
|
|
@ -39,7 +39,7 @@ interface RoomService {
|
||||||
*/
|
*/
|
||||||
fun joinRoom(roomId: String,
|
fun joinRoom(roomId: String,
|
||||||
viaServers: List<String> = emptyList(),
|
viaServers: List<String> = emptyList(),
|
||||||
callback: MatrixCallback<Unit>)
|
callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a room from a roomId
|
* Get a room from a roomId
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.room.failure
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
|
||||||
|
sealed class CreateRoomFailure : Failure.FeatureFailure() {
|
||||||
|
|
||||||
|
object CreatedWithTimeout: CreateRoomFailure()
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.room.failure
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
|
||||||
|
sealed class JoinRoomFailure : Failure.FeatureFailure() {
|
||||||
|
|
||||||
|
object JoinedWithTimeout : JoinRoomFailure()
|
||||||
|
|
||||||
|
}
|
|
@ -16,45 +16,45 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.database
|
package im.vector.matrix.android.internal.database
|
||||||
|
|
||||||
import android.os.Handler
|
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
||||||
import android.os.HandlerThread
|
|
||||||
import io.realm.*
|
import io.realm.*
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
private const val THREAD_NAME = "REALM_QUERY_LATCH"
|
|
||||||
|
|
||||||
class RealmQueryLatch<E : RealmObject>(private val realmConfiguration: RealmConfiguration,
|
class RealmQueryLatch<E : RealmObject>(private val realmConfiguration: RealmConfiguration,
|
||||||
private val realmQueryBuilder: (Realm) -> RealmQuery<E>) {
|
private val realmQueryBuilder: (Realm) -> RealmQuery<E>) {
|
||||||
|
|
||||||
@Throws(InterruptedException::class)
|
private companion object {
|
||||||
fun await(timeout: Long = Long.MAX_VALUE, timeUnit: TimeUnit = TimeUnit.MILLISECONDS) {
|
val QUERY_LATCH_HANDLER = createBackgroundHandler("REALM_QUERY_LATCH")
|
||||||
val latch = CountDownLatch(1)
|
}
|
||||||
val handlerThread = HandlerThread(THREAD_NAME + hashCode())
|
|
||||||
handlerThread.start()
|
|
||||||
val handler = Handler(handlerThread.looper)
|
|
||||||
val runnable = Runnable {
|
|
||||||
val realm = Realm.getInstance(realmConfiguration)
|
|
||||||
val result = realmQueryBuilder(realm).findAllAsync()
|
|
||||||
|
|
||||||
|
@Throws(InterruptedException::class)
|
||||||
|
fun await(timeout: Long, timeUnit: TimeUnit) {
|
||||||
|
val realmRef = AtomicReference<Realm>()
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
QUERY_LATCH_HANDLER.post {
|
||||||
|
val realm = Realm.getInstance(realmConfiguration)
|
||||||
|
realmRef.set(realm)
|
||||||
|
val result = realmQueryBuilder(realm).findAllAsync()
|
||||||
result.addChangeListener(object : RealmChangeListener<RealmResults<E>> {
|
result.addChangeListener(object : RealmChangeListener<RealmResults<E>> {
|
||||||
override fun onChange(t: RealmResults<E>) {
|
override fun onChange(t: RealmResults<E>) {
|
||||||
if (t.isNotEmpty()) {
|
if (t.isNotEmpty()) {
|
||||||
result.removeChangeListener(this)
|
result.removeChangeListener(this)
|
||||||
realm.close()
|
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
handler.post(runnable)
|
|
||||||
try {
|
try {
|
||||||
latch.await(timeout, timeUnit)
|
latch.await(timeout, timeUnit)
|
||||||
} catch (exception: InterruptedException) {
|
} catch (exception: InterruptedException) {
|
||||||
throw exception
|
throw exception
|
||||||
} finally {
|
} finally {
|
||||||
handlerThread.quit()
|
QUERY_LATCH_HANDLER.post {
|
||||||
|
realmRef.getAndSet(null).close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,8 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun joinRoom(roomId: String, viaServers: List<String>, callback: MatrixCallback<Unit>) {
|
override fun joinRoom(roomId: String, viaServers: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
joinRoomTask
|
return joinRoomTask
|
||||||
.configureWith(JoinRoomTask.Params(roomId, viaServers))
|
.configureWith(JoinRoomTask.Params(roomId, viaServers))
|
||||||
.dispatchTo(callback)
|
.dispatchTo(callback)
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package im.vector.matrix.android.internal.session.room.create
|
package im.vector.matrix.android.internal.session.room.create
|
||||||
|
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
|
import arrow.core.recoverWith
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
|
||||||
import im.vector.matrix.android.internal.database.RealmQueryLatch
|
import im.vector.matrix.android.internal.database.RealmQueryLatch
|
||||||
|
@ -57,9 +59,11 @@ internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: Ro
|
||||||
realm.where(RoomEntity::class.java)
|
realm.where(RoomEntity::class.java)
|
||||||
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
||||||
}
|
}
|
||||||
Try {
|
try {
|
||||||
rql.await(timeout = 20L, timeUnit = TimeUnit.SECONDS)
|
rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES)
|
||||||
roomId
|
Try.just(roomId)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Try.raise<String>(CreateRoomFailure.CreatedWithTimeout)
|
||||||
}
|
}
|
||||||
}.flatMap { roomId ->
|
}.flatMap { roomId ->
|
||||||
if (params.isDirect()) {
|
if (params.isDirect()) {
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package im.vector.matrix.android.internal.session.room.membership.joining
|
package im.vector.matrix.android.internal.session.room.membership.joining
|
||||||
|
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
|
import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure
|
||||||
|
import im.vector.matrix.android.api.session.room.failure.JoinRoomFailure
|
||||||
import im.vector.matrix.android.internal.database.RealmQueryLatch
|
import im.vector.matrix.android.internal.database.RealmQueryLatch
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntityFields
|
import im.vector.matrix.android.internal.database.model.RoomEntityFields
|
||||||
|
@ -50,9 +52,11 @@ internal class DefaultJoinRoomTask @Inject constructor(private val roomAPI: Room
|
||||||
realm.where(RoomEntity::class.java)
|
realm.where(RoomEntity::class.java)
|
||||||
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
||||||
}
|
}
|
||||||
Try {
|
try {
|
||||||
rql.await(20L, TimeUnit.SECONDS)
|
rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES)
|
||||||
roomId
|
Try.just(roomId)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Try.raise<String>(JoinRoomFailure.JoinedWithTimeout)
|
||||||
}
|
}
|
||||||
}.flatMap { roomId ->
|
}.flatMap { roomId ->
|
||||||
setReadMarkers(roomId)
|
setReadMarkers(roomId)
|
||||||
|
|
|
@ -30,12 +30,16 @@ class ErrorFormatter @Inject constructor(val stringProvider: StringProvider) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toHumanReadable(throwable: Throwable?): String {
|
fun toHumanReadable(throwable: Throwable?): String {
|
||||||
|
|
||||||
return when (throwable) {
|
return when (throwable) {
|
||||||
null -> ""
|
null -> ""
|
||||||
is Failure.NetworkConnection -> stringProvider.getString(R.string.error_no_network)
|
is Failure.NetworkConnection -> stringProvider.getString(R.string.error_no_network)
|
||||||
else -> throwable.localizedMessage
|
is Failure.ServerError -> {
|
||||||
|
throwable.error.message.takeIf { it.isNotEmpty() }
|
||||||
|
?: throwable.error.code.takeIf { it.isNotEmpty() }
|
||||||
|
?: stringProvider.getString(R.string.unknown_error)
|
||||||
|
}
|
||||||
|
else -> throwable.localizedMessage
|
||||||
|
?: stringProvider.getString(R.string.unknown_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -29,6 +29,7 @@ import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Loading
|
import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.viewModel
|
import com.airbnb.mvrx.viewModel
|
||||||
|
import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
|
@ -38,6 +39,7 @@ import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.platform.SimpleFragmentActivity
|
import im.vector.riotx.core.platform.SimpleFragmentActivity
|
||||||
import im.vector.riotx.core.platform.WaitingViewData
|
import im.vector.riotx.core.platform.WaitingViewData
|
||||||
import kotlinx.android.synthetic.main.activity.*
|
import kotlinx.android.synthetic.main.activity.*
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
||||||
|
@ -91,6 +93,9 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
||||||
|
|
||||||
private fun renderCreationFailure(error: Throwable) {
|
private fun renderCreationFailure(error: Throwable) {
|
||||||
hideWaitingView()
|
hideWaitingView()
|
||||||
|
if (error is CreateRoomFailure.CreatedWithTimeout) {
|
||||||
|
finish()
|
||||||
|
} else
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setMessage(errorFormatter.toHumanReadable(error))
|
.setMessage(errorFormatter.toHumanReadable(error))
|
||||||
.setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() }
|
.setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() }
|
||||||
|
|
|
@ -65,6 +65,7 @@ import com.otaliastudios.autocomplete.CharPolicy
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.room.failure.JoinRoomFailure
|
||||||
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
||||||
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.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.*
|
||||||
|
@ -608,7 +609,7 @@ class RoomDetailFragment :
|
||||||
// TODO Better handling progress
|
// TODO Better handling progress
|
||||||
vectorBaseActivity.showWaitingView()
|
vectorBaseActivity.showWaitingView()
|
||||||
vectorBaseActivity.waiting_view_status_text.visibility = View.VISIBLE
|
vectorBaseActivity.waiting_view_status_text.visibility = View.VISIBLE
|
||||||
vectorBaseActivity.waiting_view_status_text.text = getString(R.string.join)
|
vectorBaseActivity.waiting_view_status_text.text = getString(R.string.joining_room)
|
||||||
}
|
}
|
||||||
is Success -> {
|
is Success -> {
|
||||||
navigator.openRoom(vectorBaseActivity, async())
|
navigator.openRoom(vectorBaseActivity, async())
|
||||||
|
|
|
@ -140,8 +140,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
?: return
|
?: return
|
||||||
|
|
||||||
val roomId = tombstoneContent.replacementRoom
|
val roomId = tombstoneContent.replacementRoom
|
||||||
// TODO replace with rx flux
|
val isRoomJoined = session.getRoom(roomId)?.roomSummary()?.membership == Membership.JOIN
|
||||||
if (session.getRoom(roomId)?.roomSummary()?.membership == Membership.JOIN) {
|
if (isRoomJoined) {
|
||||||
setState { copy(tombstoneEventHandling = Success(roomId)) }
|
setState { copy(tombstoneEventHandling = Success(roomId)) }
|
||||||
} else {
|
} else {
|
||||||
val viaServer = MatrixPatterns.extractServerNameFromId(action.event.senderId).let {
|
val viaServer = MatrixPatterns.extractServerNameFromId(action.event.senderId).let {
|
||||||
|
@ -151,17 +151,12 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
listOf(it)
|
listOf(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setState { copy(tombstoneEventHandling = Loading()) }
|
session.rx()
|
||||||
session.joinRoom(roomId, viaServer, object : MatrixCallback<Unit> {
|
.joinRoom(roomId, viaServer)
|
||||||
override fun onSuccess(data: Unit) {
|
.map { roomId }
|
||||||
setState { copy(tombstoneEventHandling = Success(roomId)) }
|
.execute {
|
||||||
|
copy(tombstoneEventHandling = it)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
setState { copy(tombstoneEventHandling = Fail(failure)) }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,4 +7,6 @@
|
||||||
<string name="direct_room_no_known_users">"No result found, use Add by matrix ID to search on server."</string>
|
<string name="direct_room_no_known_users">"No result found, use Add by matrix ID to search on server."</string>
|
||||||
<string name="direct_room_start_search">"Start typing to get results"</string>
|
<string name="direct_room_start_search">"Start typing to get results"</string>
|
||||||
<string name="direct_room_filter_hint">"Filter by username or ID…"</string>
|
<string name="direct_room_filter_hint">"Filter by username or ID…"</string>
|
||||||
|
|
||||||
|
<string name="joining_room">"Joining room…"</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue