diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt index 758b78be..3ae8f3a8 100644 --- a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt @@ -93,6 +93,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity() { } protected fun Disposable.disposeOnDestroy(): Disposable { + // TODO Ganfra: never disposed... uiDisposables.add(this) return this } diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt index a8ca670a..d8d9331c 100644 --- a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt @@ -27,6 +27,9 @@ import butterknife.Unbinder import com.airbnb.mvrx.BaseMvRxFragment import com.airbnb.mvrx.MvRx 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 abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed { @@ -78,6 +81,12 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed { mUnBinder = null } + override fun onDestroy() { + super.onDestroy() + + uiDisposables.dispose() + } + /* ========================================================================================== * Restorable * ========================================================================================== */ @@ -114,6 +123,16 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed { return this } + /* ========================================================================================== + * Disposable + * ========================================================================================== */ + + private val uiDisposables = CompositeDisposable() + + protected fun Disposable.disposeOnDestroy(): Disposable { + uiDisposables.add(this) + return this + } /* ========================================================================================== * MENU MANAGEMENT diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt index 47fb9995..8821b1d4 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt @@ -35,6 +35,7 @@ abstract class PublicRoomItem : VectorEpoxyModel() { enum class JoinState { NOT_JOINED, JOINING, + JOINING_ERROR, JOINED } @@ -74,9 +75,11 @@ abstract class PublicRoomItem : VectorEpoxyModel() { holder.joinButton.isInvisible = true } holder.joiningView.isVisible = joinState == JoinState.JOINING + holder.retryButton.isVisible = joinState == JoinState.JOINING_ERROR holder.joinedView.isVisible = joinState == JoinState.JOINED holder.joinButton.setOnClickListener { joinListener?.invoke() } + holder.retryButton.setOnClickListener { joinListener?.invoke() } } @@ -90,6 +93,7 @@ abstract class PublicRoomItem : VectorEpoxyModel() { val joinedView by bind(R.id.itemPublicRoomJoined) val joinButton by bind(R.id.itemPublicRoomJoin) val joiningView by bind(R.id.itemPublicRoomJoining) + val retryButton by bind(R.id.itemPublicRoomRetry) } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt index f530a918..738e4e88 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt @@ -82,9 +82,10 @@ class PublicRoomsController(private val stringProvider: StringProvider) : TypedE roomName(publicRoom.name) nbOfMembers(publicRoom.numJoinedMembers) when { - viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINED) - viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING) - else -> joinState(PublicRoomItem.JoinState.NOT_JOINED) + viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINED) + viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING) + viewState.joiningErrorRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING_ERROR) + else -> joinState(PublicRoomItem.JoinState.NOT_JOINED) } joinListener { callback?.onPublicRoomJoin(publicRoom) diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt index 0d4b4ab6..5ca1ee56 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt @@ -17,29 +17,30 @@ package im.vector.riotredesign.features.roomdirectory import android.os.Bundle -import android.text.Editable import android.view.View +import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.mvrx.activityViewModel 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.riotredesign.R 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.features.roomdirectory.picker.RoomDirectoryPickerFragment +import io.reactivex.rxkotlin.subscribeBy import kotlinx.android.synthetic.main.fragment_public_rooms.* import org.koin.android.ext.android.get import timber.log.Timber +import java.util.concurrent.TimeUnit /** * 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 * - * FIXME Rotate screen launch again the request - * * TODO For Nad: * Display number of rooms? * Picto size are not correct @@ -66,12 +67,12 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback it.setDisplayHomeAsUpEnabled(true) } - publicRoomsFilter.addTextChangedListener(object : SimpleTextWatcher() { - override fun afterTextChanged(s: Editable) { - // TODO Debounce - viewModel.filterWith(publicRoomsFilter.text.toString()) - } - }) + RxTextView.textChanges(publicRoomsFilter) + .debounce(500, TimeUnit.MILLISECONDS) + .subscribeBy { + viewModel.filterWith(it.toString()) + } + .disposeOnDestroy() publicRoomsCreateNewRoom.setOnClickListener { vectorBaseActivity.notImplemented() @@ -80,6 +81,13 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback publicRoomsChangeDirectory.setOnClickListener { 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?) { diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewModel.kt index 32a3e25e..6f04f786 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewModel.kt @@ -16,6 +16,8 @@ package im.vector.riotredesign.features.roomdirectory +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import com.airbnb.mvrx.* import im.vector.matrix.android.api.MatrixCallback 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.rx.rx import im.vector.riotredesign.core.platform.VectorViewModel +import im.vector.riotredesign.core.utils.LiveEvent import org.koin.android.ext.android.get import timber.log.Timber @@ -46,6 +49,11 @@ class RoomDirectoryViewModel(initialState: PublicRoomsViewState, } } + private val _joinRoomErrorLiveData = MutableLiveData>() + val joinRoomErrorLiveData: LiveData> + get() = _joinRoomErrorLiveData + + // TODO Store in ViewState? private var currentFilter: String = "" @@ -85,7 +93,9 @@ class RoomDirectoryViewModel(initialState: PublicRoomsViewState, copy( joinedRoomsIds = joinedRoomIds, // 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) { - // TODO Notify the user + // Notify the user + _joinRoomErrorLiveData.postValue(LiveEvent(failure)) + setState { copy( - joiningRoomsIds = joiningRoomsIds.toMutableList().apply { remove(publicRoom.roomId) } + joiningRoomsIds = joiningRoomsIds.toMutableList().apply { remove(publicRoom.roomId) }, + joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableList().apply { add(publicRoom.roomId) } ) } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewState.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewState.kt index 3c02a1f1..6fc7f519 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewState.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsViewState.kt @@ -30,6 +30,8 @@ data class PublicRoomsViewState( val hasMore: Boolean = false, // List of roomIds that the user wants to join val joiningRoomsIds: List = emptyList(), + // List of roomIds that the user wants to join, but an error occurred + val joiningErrorRoomsIds: List = emptyList(), // List of joined roomId, val joinedRoomsIds: List = emptyList(), val roomDirectoryDisplayName: String? = null diff --git a/vector/src/main/res/layout/fragment_public_rooms.xml b/vector/src/main/res/layout/fragment_public_rooms.xml index 8948af4c..5ace931b 100644 --- a/vector/src/main/res/layout/fragment_public_rooms.xml +++ b/vector/src/main/res/layout/fragment_public_rooms.xml @@ -3,6 +3,7 @@ diff --git a/vector/src/main/res/layout/item_public_room.xml b/vector/src/main/res/layout/item_public_room.xml index 4d0cfe57..eb1f44ee 100644 --- a/vector/src/main/res/layout/item_public_room.xml +++ b/vector/src/main/res/layout/item_public_room.xml @@ -93,6 +93,18 @@ app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin" app:layout_constraintTop_toTopOf="parent" /> +