Retry join room

This commit is contained in:
Benoit Marty 2019-05-24 17:38:46 +02:00
parent bbf2f96288
commit cd5e808bb6
9 changed files with 77 additions and 16 deletions

View File

@ -93,6 +93,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
} }


protected fun Disposable.disposeOnDestroy(): Disposable { protected fun Disposable.disposeOnDestroy(): Disposable {
// TODO Ganfra: never disposed...
uiDisposables.add(this) uiDisposables.add(this)
return this return this
} }

View File

@ -27,6 +27,9 @@ import butterknife.Unbinder
import com.airbnb.mvrx.BaseMvRxFragment import com.airbnb.mvrx.BaseMvRxFragment
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.bumptech.glide.util.Util.assertMainThread import com.bumptech.glide.util.Util.assertMainThread
import com.google.android.material.snackbar.Snackbar
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import timber.log.Timber import timber.log.Timber


abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed { abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
@ -78,6 +81,12 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
mUnBinder = null mUnBinder = null
} }


override fun onDestroy() {
super.onDestroy()

uiDisposables.dispose()
}

/* ========================================================================================== /* ==========================================================================================
* Restorable * Restorable
* ========================================================================================== */ * ========================================================================================== */
@ -114,6 +123,16 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
return this return this
} }


/* ==========================================================================================
* Disposable
* ========================================================================================== */

private val uiDisposables = CompositeDisposable()

protected fun Disposable.disposeOnDestroy(): Disposable {
uiDisposables.add(this)
return this
}


/* ========================================================================================== /* ==========================================================================================
* MENU MANAGEMENT * MENU MANAGEMENT

View File

@ -35,6 +35,7 @@ abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
enum class JoinState { enum class JoinState {
NOT_JOINED, NOT_JOINED,
JOINING, JOINING,
JOINING_ERROR,
JOINED JOINED
} }


@ -74,9 +75,11 @@ abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
holder.joinButton.isInvisible = true holder.joinButton.isInvisible = true
} }
holder.joiningView.isVisible = joinState == JoinState.JOINING holder.joiningView.isVisible = joinState == JoinState.JOINING
holder.retryButton.isVisible = joinState == JoinState.JOINING_ERROR
holder.joinedView.isVisible = joinState == JoinState.JOINED holder.joinedView.isVisible = joinState == JoinState.JOINED


holder.joinButton.setOnClickListener { joinListener?.invoke() } holder.joinButton.setOnClickListener { joinListener?.invoke() }
holder.retryButton.setOnClickListener { joinListener?.invoke() }
} }




@ -90,6 +93,7 @@ abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
val joinedView by bind<View>(R.id.itemPublicRoomJoined) val joinedView by bind<View>(R.id.itemPublicRoomJoined)
val joinButton by bind<View>(R.id.itemPublicRoomJoin) val joinButton by bind<View>(R.id.itemPublicRoomJoin)
val joiningView by bind<View>(R.id.itemPublicRoomJoining) val joiningView by bind<View>(R.id.itemPublicRoomJoining)
val retryButton by bind<View>(R.id.itemPublicRoomRetry)
} }


} }

View File

@ -82,9 +82,10 @@ class PublicRoomsController(private val stringProvider: StringProvider) : TypedE
roomName(publicRoom.name) roomName(publicRoom.name)
nbOfMembers(publicRoom.numJoinedMembers) nbOfMembers(publicRoom.numJoinedMembers)
when { when {
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINED) viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINED)
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING) viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING)
else -> joinState(PublicRoomItem.JoinState.NOT_JOINED) viewState.joiningErrorRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING_ERROR)
else -> joinState(PublicRoomItem.JoinState.NOT_JOINED)
} }
joinListener { joinListener {
callback?.onPublicRoomJoin(publicRoom) callback?.onPublicRoomJoin(publicRoom)

View File

@ -17,29 +17,30 @@
package im.vector.riotredesign.features.roomdirectory package im.vector.riotredesign.features.roomdirectory


import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.view.View import android.view.View
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.epoxy.EpoxyVisibilityTracker
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import com.google.android.material.snackbar.Snackbar
import com.jakewharton.rxbinding2.widget.RxTextView
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.addFragmentToBackstack import im.vector.riotredesign.core.extensions.addFragmentToBackstack
import im.vector.riotredesign.core.platform.SimpleTextWatcher
import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.android.synthetic.main.fragment_public_rooms.* import kotlinx.android.synthetic.main.fragment_public_rooms.*
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit




/** /**
* What can be improved: * What can be improved:
* - When filtering more (when entering new chars), we could filter on result we already have, during the new server request, to avoid empty screen effect * - When filtering more (when entering new chars), we could filter on result we already have, during the new server request, to avoid empty screen effect
* *
* FIXME Rotate screen launch again the request
*
* TODO For Nad: * TODO For Nad:
* Display number of rooms? * Display number of rooms?
* Picto size are not correct * Picto size are not correct
@ -66,12 +67,12 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
it.setDisplayHomeAsUpEnabled(true) it.setDisplayHomeAsUpEnabled(true)
} }


publicRoomsFilter.addTextChangedListener(object : SimpleTextWatcher() { RxTextView.textChanges(publicRoomsFilter)
override fun afterTextChanged(s: Editable) { .debounce(500, TimeUnit.MILLISECONDS)
// TODO Debounce .subscribeBy {
viewModel.filterWith(publicRoomsFilter.text.toString()) viewModel.filterWith(it.toString())
} }
}) .disposeOnDestroy()


publicRoomsCreateNewRoom.setOnClickListener { publicRoomsCreateNewRoom.setOnClickListener {
vectorBaseActivity.notImplemented() vectorBaseActivity.notImplemented()
@ -80,6 +81,13 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
publicRoomsChangeDirectory.setOnClickListener { publicRoomsChangeDirectory.setOnClickListener {
vectorBaseActivity.addFragmentToBackstack(RoomDirectoryPickerFragment(), R.id.simpleFragmentContainer) vectorBaseActivity.addFragmentToBackstack(RoomDirectoryPickerFragment(), R.id.simpleFragmentContainer)
} }

viewModel.joinRoomErrorLiveData.observe(this, Observer {
it.getContentIfNotHandled()?.let { throwable ->
Snackbar.make(publicRoomsCoordinator, throwable.localizedMessage, Snackbar.LENGTH_SHORT)
.show()
}
})
} }


override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {

View File

@ -16,6 +16,8 @@


package im.vector.riotredesign.features.roomdirectory package im.vector.riotredesign.features.roomdirectory


import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.* import com.airbnb.mvrx.*
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
@ -28,6 +30,7 @@ import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryD
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.platform.VectorViewModel
import im.vector.riotredesign.core.utils.LiveEvent
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
import timber.log.Timber import timber.log.Timber


@ -46,6 +49,11 @@ class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
} }
} }


private val _joinRoomErrorLiveData = MutableLiveData<LiveEvent<Throwable>>()
val joinRoomErrorLiveData: LiveData<LiveEvent<Throwable>>
get() = _joinRoomErrorLiveData


// TODO Store in ViewState? // TODO Store in ViewState?
private var currentFilter: String = "" private var currentFilter: String = ""


@ -85,7 +93,9 @@ class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
copy( copy(
joinedRoomsIds = joinedRoomIds, joinedRoomsIds = joinedRoomIds,
// Remove (newly) joined room id from the joining room list // Remove (newly) joined room id from the joining room list
joiningRoomsIds = joiningRoomsIds.toMutableList().apply { removeAll(joinedRoomIds) } joiningRoomsIds = joiningRoomsIds.toMutableList().apply { removeAll(joinedRoomIds) },
// Remove (newly) joined room id from the joining room list in error
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableList().apply { removeAll(joinedRoomIds) }
) )
} }
} }
@ -197,10 +207,13 @@ class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
} }


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
// TODO Notify the user // Notify the user
_joinRoomErrorLiveData.postValue(LiveEvent(failure))

setState { setState {
copy( copy(
joiningRoomsIds = joiningRoomsIds.toMutableList().apply { remove(publicRoom.roomId) } joiningRoomsIds = joiningRoomsIds.toMutableList().apply { remove(publicRoom.roomId) },
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableList().apply { add(publicRoom.roomId) }
) )
} }
} }

View File

@ -30,6 +30,8 @@ data class PublicRoomsViewState(
val hasMore: Boolean = false, val hasMore: Boolean = false,
// List of roomIds that the user wants to join // List of roomIds that the user wants to join
val joiningRoomsIds: List<String> = emptyList(), val joiningRoomsIds: List<String> = emptyList(),
// List of roomIds that the user wants to join, but an error occurred
val joiningErrorRoomsIds: List<String> = emptyList(),
// List of joined roomId, // List of joined roomId,
val joinedRoomsIds: List<String> = emptyList(), val joinedRoomsIds: List<String> = emptyList(),
val roomDirectoryDisplayName: String? = null val roomDirectoryDisplayName: String? = null

View File

@ -3,6 +3,7 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/publicRoomsCoordinator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/pale_grey"> android:background="@color/pale_grey">

View File

@ -93,6 +93,18 @@
app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin" app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />


<Button
android:id="@+id/itemPublicRoomRetry"
style="@style/VectorButtonStyleFlat"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="@string/global_retry"
android:textColor="@color/vector_warning_color"
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
app:layout_constraintEnd_toEndOf="@+id/itemPublicRoomJoin"
app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin"
app:layout_constraintTop_toTopOf="parent" />

<View <View
android:id="@+id/itemPublicRoomBottomSeparator" android:id="@+id/itemPublicRoomBottomSeparator"
android:layout_width="0dp" android:layout_width="0dp"