Handle room invitation actions
This commit is contained in:
parent
07309c90e1
commit
01e3e71f98
|
@ -47,7 +47,7 @@ interface MembershipService {
|
||||||
*/
|
*/
|
||||||
fun getRoomMemberIdsLive(): LiveData<List<String>>
|
fun getRoomMemberIdsLive(): LiveData<List<String>>
|
||||||
|
|
||||||
fun getNumberOfJoinedMembers() : Int
|
fun getNumberOfJoinedMembers(): Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invite a user in the room
|
* Invite a user in the room
|
||||||
|
@ -55,13 +55,12 @@ interface MembershipService {
|
||||||
fun invite(userId: String, callback: MatrixCallback<Unit>)
|
fun invite(userId: String, callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join the room
|
* Join the room, or accept an invitation.
|
||||||
*/
|
*/
|
||||||
fun join(callback: MatrixCallback<Unit>)
|
fun join(callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leave the room.
|
* Leave the room, or reject an invitation.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
fun leave(callback: MatrixCallback<Unit>)
|
fun leave(callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,16 @@
|
||||||
package im.vector.riotredesign.features.home.room.list
|
package im.vector.riotredesign.features.home.room.list
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.riotredesign.core.extensions.setTextOrHide
|
import im.vector.riotredesign.core.extensions.setTextOrHide
|
||||||
|
import im.vector.riotredesign.core.platform.ButtonStateView
|
||||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +39,10 @@ abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>(
|
||||||
@EpoxyAttribute var secondLine: CharSequence? = null
|
@EpoxyAttribute var secondLine: CharSequence? = null
|
||||||
@EpoxyAttribute var avatarUrl: String? = null
|
@EpoxyAttribute var avatarUrl: String? = null
|
||||||
@EpoxyAttribute var listener: (() -> Unit)? = null
|
@EpoxyAttribute var listener: (() -> Unit)? = null
|
||||||
|
@EpoxyAttribute var invitationAcceptInProgress: Boolean = false
|
||||||
|
@EpoxyAttribute var invitationAcceptInError: Boolean = false
|
||||||
|
@EpoxyAttribute var invitationRejectInProgress: Boolean = false
|
||||||
|
@EpoxyAttribute var invitationRejectInError: Boolean = false
|
||||||
@EpoxyAttribute var acceptListener: (() -> Unit)? = null
|
@EpoxyAttribute var acceptListener: (() -> Unit)? = null
|
||||||
@EpoxyAttribute var rejectListener: (() -> Unit)? = null
|
@EpoxyAttribute var rejectListener: (() -> Unit)? = null
|
||||||
|
|
||||||
|
@ -45,8 +50,44 @@ abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>(
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.rootView.setOnClickListener { listener?.invoke() }
|
holder.rootView.setOnClickListener { listener?.invoke() }
|
||||||
holder.acceptView.setOnClickListener { acceptListener?.invoke() }
|
|
||||||
holder.rejectView.setOnClickListener { rejectListener?.invoke() }
|
// When a request is in progress (accept or reject), we only use the accept State button
|
||||||
|
val requestInProgress = invitationAcceptInProgress || invitationRejectInProgress
|
||||||
|
|
||||||
|
when {
|
||||||
|
requestInProgress -> holder.acceptView.render(ButtonStateView.State.Loading)
|
||||||
|
invitationAcceptInError -> holder.acceptView.render(ButtonStateView.State.Error)
|
||||||
|
else -> holder.acceptView.render(ButtonStateView.State.Button)
|
||||||
|
}
|
||||||
|
// ButtonStateView.State.Loaded not used because roomSummary will not be displayed as a room invitation anymore
|
||||||
|
|
||||||
|
|
||||||
|
holder.acceptView.callback = object : ButtonStateView.Callback {
|
||||||
|
override fun onButtonClicked() {
|
||||||
|
acceptListener?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRetryClicked() {
|
||||||
|
acceptListener?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.rejectView.isVisible = !requestInProgress
|
||||||
|
|
||||||
|
when {
|
||||||
|
invitationRejectInError -> holder.rejectView.render(ButtonStateView.State.Error)
|
||||||
|
else -> holder.rejectView.render(ButtonStateView.State.Button)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.rejectView.callback = object : ButtonStateView.Callback {
|
||||||
|
override fun onButtonClicked() {
|
||||||
|
rejectListener?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRetryClicked() {
|
||||||
|
rejectListener?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
holder.titleView.text = roomName
|
holder.titleView.text = roomName
|
||||||
holder.subtitleView.setTextOrHide(secondLine)
|
holder.subtitleView.setTextOrHide(secondLine)
|
||||||
avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
|
avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
|
||||||
|
@ -55,8 +96,8 @@ abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>(
|
||||||
class Holder : VectorEpoxyHolder() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
val titleView by bind<TextView>(R.id.roomInvitationNameView)
|
val titleView by bind<TextView>(R.id.roomInvitationNameView)
|
||||||
val subtitleView by bind<TextView>(R.id.roomInvitationSubTitle)
|
val subtitleView by bind<TextView>(R.id.roomInvitationSubTitle)
|
||||||
val acceptView by bind<Button>(R.id.roomInvitationAccept)
|
val acceptView by bind<ButtonStateView>(R.id.roomInvitationAccept)
|
||||||
val rejectView by bind<Button>(R.id.roomInvitationReject)
|
val rejectView by bind<ButtonStateView>(R.id.roomInvitationReject)
|
||||||
val avatarImageView by bind<ImageView>(R.id.roomInvitationAvatarImageView)
|
val avatarImageView by bind<ImageView>(R.id.roomInvitationAvatarImageView)
|
||||||
val rootView by bind<ViewGroup>(R.id.itemRoomInvitationLayout)
|
val rootView by bind<ViewGroup>(R.id.itemRoomInvitationLayout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,4 +24,8 @@ sealed class RoomListActions {
|
||||||
|
|
||||||
data class ToggleCategory(val category: RoomCategory) : RoomListActions()
|
data class ToggleCategory(val category: RoomCategory) : RoomListActions()
|
||||||
|
|
||||||
|
data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListActions()
|
||||||
|
|
||||||
|
data class RejectInvitation(val roomSummary: RoomSummary) : RoomListActions()
|
||||||
|
|
||||||
}
|
}
|
|
@ -24,12 +24,15 @@ import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.airbnb.mvrx.*
|
import com.airbnb.mvrx.*
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
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.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.di.ScreenComponent
|
import im.vector.riotredesign.core.di.ScreenComponent
|
||||||
import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
|
||||||
|
import im.vector.riotredesign.core.error.ErrorFormatter
|
||||||
|
import im.vector.riotredesign.core.extensions.observeEvent
|
||||||
import im.vector.riotredesign.core.extensions.observeEventDebounced
|
import im.vector.riotredesign.core.extensions.observeEventDebounced
|
||||||
import im.vector.riotredesign.core.platform.OnBackPressed
|
import im.vector.riotredesign.core.platform.OnBackPressed
|
||||||
import im.vector.riotredesign.core.platform.StateView
|
import im.vector.riotredesign.core.platform.StateView
|
||||||
|
@ -64,6 +67,7 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
private val roomListParams: RoomListParams by args()
|
private val roomListParams: RoomListParams by args()
|
||||||
@Inject lateinit var roomController: RoomSummaryController
|
@Inject lateinit var roomController: RoomSummaryController
|
||||||
@Inject lateinit var roomListViewModelFactory: RoomListViewModel.Factory
|
@Inject lateinit var roomListViewModelFactory: RoomListViewModel.Factory
|
||||||
|
@Inject lateinit var errorFormatter: ErrorFormatter
|
||||||
private val roomListViewModel: RoomListViewModel by fragmentViewModel()
|
private val roomListViewModel: RoomListViewModel by fragmentViewModel()
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.fragment_room_list
|
override fun getLayoutResId() = R.layout.fragment_room_list
|
||||||
|
@ -82,6 +86,13 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
}
|
}
|
||||||
|
|
||||||
createChatFabMenu.listener = this
|
createChatFabMenu.listener = this
|
||||||
|
|
||||||
|
roomListViewModel.invitationAnswerErrorLiveData.observeEvent(this) { throwable ->
|
||||||
|
vectorBaseActivity.coordinatorLayout?.let {
|
||||||
|
Snackbar.make(it, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupCreateRoomButton() {
|
private fun setupCreateRoomButton() {
|
||||||
|
@ -234,11 +245,11 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAcceptRoomInvitation(room: RoomSummary) {
|
override fun onAcceptRoomInvitation(room: RoomSummary) {
|
||||||
vectorBaseActivity.notImplemented("Accept room invitation")
|
roomListViewModel.accept(RoomListActions.AcceptInvitation(room))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRejectRoomInvitation(room: RoomSummary) {
|
override fun onRejectRoomInvitation(room: RoomSummary) {
|
||||||
vectorBaseActivity.notImplemented("Reject room invitation")
|
roomListViewModel.accept(RoomListActions.RejectInvitation(room))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
||||||
|
|
|
@ -23,14 +23,18 @@ import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
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.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||||
import im.vector.riotredesign.core.utils.LiveEvent
|
import im.vector.riotredesign.core.utils.LiveEvent
|
||||||
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class RoomListViewModel @AssistedInject constructor(@Assisted initialState: RoomListViewState,
|
class RoomListViewModel @AssistedInject constructor(@Assisted initialState: RoomListViewState,
|
||||||
|
private val session: Session,
|
||||||
private val homeRoomListObservableSource: HomeRoomListObservableStore,
|
private val homeRoomListObservableSource: HomeRoomListObservableStore,
|
||||||
private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
|
private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
|
||||||
private val chronologicalRoomComparator: ChronologicalRoomComparator)
|
private val chronologicalRoomComparator: ChronologicalRoomComparator)
|
||||||
|
@ -56,14 +60,20 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
|
||||||
val openRoomLiveData: LiveData<LiveEvent<String>>
|
val openRoomLiveData: LiveData<LiveEvent<String>>
|
||||||
get() = _openRoomLiveData
|
get() = _openRoomLiveData
|
||||||
|
|
||||||
|
private val _invitationAnswerErrorLiveData = MutableLiveData<LiveEvent<Throwable>>()
|
||||||
|
val invitationAnswerErrorLiveData: LiveData<LiveEvent<Throwable>>
|
||||||
|
get() = _invitationAnswerErrorLiveData
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeRoomSummaries()
|
observeRoomSummaries()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(action: RoomListActions) {
|
fun accept(action: RoomListActions) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomListActions.SelectRoom -> handleSelectRoom(action)
|
is RoomListActions.SelectRoom -> handleSelectRoom(action)
|
||||||
is RoomListActions.ToggleCategory -> handleToggleCategory(action)
|
is RoomListActions.ToggleCategory -> handleToggleCategory(action)
|
||||||
|
is RoomListActions.AcceptInvitation -> handleAcceptInvitation(action)
|
||||||
|
is RoomListActions.RejectInvitation -> handleRejectInvitation(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +102,78 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleAcceptInvitation(action: RoomListActions.AcceptInvitation) = withState { state ->
|
||||||
|
val roomId = action.roomSummary.roomId
|
||||||
|
|
||||||
|
if (state.joiningRoomsIds.contains(roomId) || state.rejectingRoomsIds.contains(roomId)) {
|
||||||
|
// Request already sent, should not happen
|
||||||
|
Timber.w("Try to join an already joining room. Should not happen")
|
||||||
|
return@withState
|
||||||
|
}
|
||||||
|
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { add(roomId) },
|
||||||
|
rejectingErrorRoomsIds = rejectingErrorRoomsIds.toMutableSet().apply { remove(roomId) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getRoom(roomId)?.join(object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
|
// Instead, we wait for the room to be joined
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
// Notify the user
|
||||||
|
_invitationAnswerErrorLiveData.postValue(LiveEvent(failure))
|
||||||
|
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(roomId) },
|
||||||
|
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(roomId) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRejectInvitation(action: RoomListActions.RejectInvitation) = withState { state ->
|
||||||
|
val roomId = action.roomSummary.roomId
|
||||||
|
|
||||||
|
if (state.joiningRoomsIds.contains(roomId) || state.rejectingRoomsIds.contains(roomId)) {
|
||||||
|
// Request already sent, should not happen
|
||||||
|
Timber.w("Try to reject an already rejecting room. Should not happen")
|
||||||
|
return@withState
|
||||||
|
}
|
||||||
|
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
rejectingRoomsIds = rejectingRoomsIds.toMutableSet().apply { add(roomId) },
|
||||||
|
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { remove(roomId) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getRoom(roomId)?.leave(object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
|
// Instead, we wait for the room to be joined
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
// Notify the user
|
||||||
|
_invitationAnswerErrorLiveData.postValue(LiveEvent(failure))
|
||||||
|
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
rejectingRoomsIds = rejectingRoomsIds.toMutableSet().apply { remove(roomId) },
|
||||||
|
rejectingErrorRoomsIds = rejectingErrorRoomsIds.toMutableSet().apply { add(roomId) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
|
private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
|
||||||
val invites = ArrayList<RoomSummary>()
|
val invites = ArrayList<RoomSummary>()
|
||||||
val favourites = ArrayList<RoomSummary>()
|
val favourites = ArrayList<RoomSummary>()
|
||||||
|
|
|
@ -27,6 +27,14 @@ data class RoomListViewState(
|
||||||
val displayMode: RoomListFragment.DisplayMode,
|
val displayMode: RoomListFragment.DisplayMode,
|
||||||
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
||||||
val asyncFilteredRooms: Async<RoomSummaries> = Uninitialized,
|
val asyncFilteredRooms: Async<RoomSummaries> = Uninitialized,
|
||||||
|
// List of roomIds that the user wants to join
|
||||||
|
val joiningRoomsIds: Set<String> = emptySet(),
|
||||||
|
// List of roomIds that the user wants to join, but an error occurred
|
||||||
|
val joiningErrorRoomsIds: Set<String> = emptySet(),
|
||||||
|
// List of roomIds that the user wants to join
|
||||||
|
val rejectingRoomsIds: Set<String> = emptySet(),
|
||||||
|
// List of roomIds that the user wants to reject, but an error occurred
|
||||||
|
val rejectingErrorRoomsIds: Set<String> = emptySet(),
|
||||||
val isInviteExpanded: Boolean = true,
|
val isInviteExpanded: Boolean = true,
|
||||||
val isFavouriteRoomsExpanded: Boolean = true,
|
val isFavouriteRoomsExpanded: Boolean = true,
|
||||||
val isDirectRoomsExpanded: Boolean = true,
|
val isDirectRoomsExpanded: Boolean = true,
|
||||||
|
|
|
@ -39,7 +39,11 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
listener?.onToggleRoomCategory(category)
|
listener?.onToggleRoomCategory(category)
|
||||||
}
|
}
|
||||||
if (isExpanded) {
|
if (isExpanded) {
|
||||||
buildRoomModels(summaries)
|
buildRoomModels(summaries,
|
||||||
|
viewState.joiningRoomsIds,
|
||||||
|
viewState.joiningErrorRoomsIds,
|
||||||
|
viewState.rejectingRoomsIds,
|
||||||
|
viewState.rejectingErrorRoomsIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,10 +77,14 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildRoomModels(summaries: List<RoomSummary>) {
|
private fun buildRoomModels(summaries: List<RoomSummary>,
|
||||||
|
joiningRoomsIds: Set<String>,
|
||||||
|
joiningErrorRoomsIds: Set<String>,
|
||||||
|
rejectingRoomsIds: Set<String>,
|
||||||
|
rejectingErrorRoomsIds: Set<String>) {
|
||||||
summaries.forEach { roomSummary ->
|
summaries.forEach { roomSummary ->
|
||||||
roomSummaryItemFactory
|
roomSummaryItemFactory
|
||||||
.create(roomSummary, listener)
|
.create(roomSummary, joiningRoomsIds, joiningErrorRoomsIds, rejectingRoomsIds, rejectingErrorRoomsIds, listener)
|
||||||
.addTo(this)
|
.addTo(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,15 +40,24 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val avatarRenderer: AvatarRenderer) {
|
private val avatarRenderer: AvatarRenderer) {
|
||||||
|
|
||||||
fun create(roomSummary: RoomSummary, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
fun create(roomSummary: RoomSummary,
|
||||||
|
joiningRoomsIds: Set<String>,
|
||||||
|
joiningErrorRoomsIds: Set<String>,
|
||||||
|
rejectingRoomsIds: Set<String>,
|
||||||
|
rejectingErrorRoomsIds: Set<String>,
|
||||||
|
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
||||||
return when (roomSummary.membership) {
|
return when (roomSummary.membership) {
|
||||||
Membership.INVITE -> createInvitationItem(roomSummary, listener)
|
Membership.INVITE -> createInvitationItem(roomSummary, joiningRoomsIds, joiningErrorRoomsIds, rejectingRoomsIds, rejectingErrorRoomsIds, listener)
|
||||||
else -> createRoomItem(roomSummary, listener)
|
else -> createRoomItem(roomSummary, listener)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createInvitationItem(roomSummary: RoomSummary, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
private fun createInvitationItem(roomSummary: RoomSummary,
|
||||||
|
joiningRoomsIds: Set<String>,
|
||||||
|
joiningErrorRoomsIds: Set<String>,
|
||||||
|
rejectingRoomsIds: Set<String>,
|
||||||
|
rejectingErrorRoomsIds: Set<String>,
|
||||||
|
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
||||||
val secondLine = if (roomSummary.isDirect) {
|
val secondLine = if (roomSummary.isDirect) {
|
||||||
roomSummary.latestEvent?.root?.senderId
|
roomSummary.latestEvent?.root?.senderId
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,6 +71,10 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte
|
||||||
.avatarRenderer(avatarRenderer)
|
.avatarRenderer(avatarRenderer)
|
||||||
.roomId(roomSummary.roomId)
|
.roomId(roomSummary.roomId)
|
||||||
.secondLine(secondLine)
|
.secondLine(secondLine)
|
||||||
|
.invitationAcceptInProgress(joiningRoomsIds.contains(roomSummary.roomId))
|
||||||
|
.invitationAcceptInError(joiningErrorRoomsIds.contains(roomSummary.roomId))
|
||||||
|
.invitationRejectInProgress(rejectingRoomsIds.contains(roomSummary.roomId))
|
||||||
|
.invitationRejectInError(rejectingErrorRoomsIds.contains(roomSummary.roomId))
|
||||||
.acceptListener { listener?.onAcceptRoomInvitation(roomSummary) }
|
.acceptListener { listener?.onAcceptRoomInvitation(roomSummary) }
|
||||||
.rejectListener { listener?.onRejectRoomInvitation(roomSummary) }
|
.rejectListener { listener?.onRejectRoomInvitation(roomSummary) }
|
||||||
.roomName(roomSummary.displayName)
|
.roomName(roomSummary.displayName)
|
||||||
|
|
|
@ -28,11 +28,11 @@ data class PublicRoomsViewState(
|
||||||
val asyncPublicRoomsRequest: Async<List<PublicRoom>> = Uninitialized,
|
val asyncPublicRoomsRequest: Async<List<PublicRoom>> = Uninitialized,
|
||||||
// True if more result are available server side
|
// True if more result are available server side
|
||||||
val hasMore: Boolean = false,
|
val hasMore: Boolean = false,
|
||||||
// List of roomIds that the user wants to join
|
// Set of roomIds that the user wants to join
|
||||||
val joiningRoomsIds: List<String> = emptyList(),
|
val joiningRoomsIds: Set<String> = emptySet(),
|
||||||
// List of roomIds that the user wants to join, but an error occurred
|
// Set of roomIds that the user wants to join, but an error occurred
|
||||||
val joiningErrorRoomsIds: List<String> = emptyList(),
|
val joiningErrorRoomsIds: Set<String> = emptySet(),
|
||||||
// List of joined roomId,
|
// Set of joined roomId,
|
||||||
val joinedRoomsIds: List<String> = emptyList(),
|
val joinedRoomsIds: Set<String> = emptySet(),
|
||||||
val roomDirectoryDisplayName: String? = null
|
val roomDirectoryDisplayName: String? = null
|
||||||
) : MvRxState
|
) : MvRxState
|
|
@ -18,13 +18,7 @@ package im.vector.riotredesign.features.roomdirectory
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.airbnb.mvrx.ActivityViewModelContext
|
import com.airbnb.mvrx.*
|
||||||
import com.airbnb.mvrx.Fail
|
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
|
||||||
import com.airbnb.mvrx.Success
|
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
|
||||||
import com.airbnb.mvrx.appendAt
|
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
@ -95,19 +89,19 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
.liveRoomSummaries()
|
.liveRoomSummaries()
|
||||||
.subscribe { list ->
|
.subscribe { list ->
|
||||||
val joinedRoomIds = list
|
val joinedRoomIds = list
|
||||||
// Keep only joined room
|
// Keep only joined room
|
||||||
?.filter { it.membership == Membership.JOIN }
|
?.filter { it.membership == Membership.JOIN }
|
||||||
?.map { it.roomId }
|
?.map { it.roomId }
|
||||||
?.toList()
|
?.toSet()
|
||||||
?: emptyList()
|
?: emptySet()
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
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.toMutableSet().apply { removeAll(joinedRoomIds) },
|
||||||
// Remove (newly) joined room id from the joining room list in error
|
// Remove (newly) joined room id from the joining room list in error
|
||||||
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableList().apply { removeAll(joinedRoomIds) }
|
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { removeAll(joinedRoomIds) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,39 +160,39 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
|
|
||||||
private fun load() {
|
private fun load() {
|
||||||
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
||||||
PublicRoomsParams(
|
PublicRoomsParams(
|
||||||
limit = PUBLIC_ROOMS_LIMIT,
|
limit = PUBLIC_ROOMS_LIMIT,
|
||||||
filter = PublicRoomsFilter(searchTerm = currentFilter),
|
filter = PublicRoomsFilter(searchTerm = currentFilter),
|
||||||
includeAllNetworks = roomDirectoryData.includeAllNetworks,
|
includeAllNetworks = roomDirectoryData.includeAllNetworks,
|
||||||
since = since,
|
since = since,
|
||||||
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
|
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
|
||||||
),
|
),
|
||||||
object : MatrixCallback<PublicRoomsResponse> {
|
object : MatrixCallback<PublicRoomsResponse> {
|
||||||
override fun onSuccess(data: PublicRoomsResponse) {
|
override fun onSuccess(data: PublicRoomsResponse) {
|
||||||
currentTask = null
|
currentTask = null
|
||||||
|
|
||||||
since = data.nextBatch
|
since = data.nextBatch
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
asyncPublicRoomsRequest = Success(data.chunk!!),
|
asyncPublicRoomsRequest = Success(data.chunk!!),
|
||||||
// It's ok to append at the end of the list, so I use publicRooms.size()
|
// It's ok to append at the end of the list, so I use publicRooms.size()
|
||||||
publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size),
|
publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size),
|
||||||
hasMore = since != null
|
hasMore = since != null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
currentTask = null
|
currentTask = null
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
asyncPublicRoomsRequest = Fail(failure)
|
asyncPublicRoomsRequest = Fail(failure)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun joinRoom(publicRoom: PublicRoom) = withState { state ->
|
fun joinRoom(publicRoom: PublicRoom) = withState { state ->
|
||||||
|
@ -210,7 +204,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
joiningRoomsIds = joiningRoomsIds.toMutableList().apply { add(publicRoom.roomId) }
|
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { add(publicRoom.roomId) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,8 +220,8 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
joiningRoomsIds = joiningRoomsIds.toMutableList().apply { remove(publicRoom.roomId) },
|
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(publicRoom.roomId) },
|
||||||
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableList().apply { add(publicRoom.roomId) }
|
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(publicRoom.roomId) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
tools:openDrawer="start">
|
tools:openDrawer="start">
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/coordinatorLayout"
|
android:id="@+id/vector_coordinator_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
|
|
@ -77,23 +77,29 @@
|
||||||
app:layout_constraintTop_toBottomOf="@+id/roomInvitationSubTitle"
|
app:layout_constraintTop_toBottomOf="@+id/roomInvitationSubTitle"
|
||||||
tools:layout_marginStart="120dp" />
|
tools:layout_marginStart="120dp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<im.vector.riotredesign.core.platform.ButtonStateView
|
||||||
android:id="@+id/roomInvitationAccept"
|
android:id="@+id/roomInvitationAccept"
|
||||||
style="@style/VectorButtonStyle"
|
android:layout_width="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
android:minWidth="122dp"
|
android:minWidth="122dp"
|
||||||
android:text="@string/accept"
|
app:bsv_button_text="@string/accept"
|
||||||
|
app:bsv_loaded_image_src="@drawable/ic_tick"
|
||||||
|
app:bsv_use_flat_button="false"
|
||||||
app:layout_constraintEnd_toEndOf="@+id/roomInvitationNameView"
|
app:layout_constraintEnd_toEndOf="@+id/roomInvitationNameView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/roomLastEventBottomSpace" />
|
app:layout_constraintTop_toBottomOf="@+id/roomLastEventBottomSpace" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<im.vector.riotredesign.core.platform.ButtonStateView
|
||||||
android:id="@+id/roomInvitationReject"
|
android:id="@+id/roomInvitationReject"
|
||||||
style="@style/VectorButtonStyleOutlined"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="@dimen/layout_vertical_margin"
|
android:layout_marginEnd="@dimen/layout_vertical_margin"
|
||||||
android:minWidth="122dp"
|
android:minWidth="122dp"
|
||||||
android:text="@string/reject"
|
app:bsv_button_text="@string/reject"
|
||||||
android:textAllCaps="true"
|
app:bsv_loaded_image_src="@drawable/ic_tick"
|
||||||
android:textColor="?riotx_text_primary"
|
app:bsv_use_flat_button="true"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/roomInvitationAccept"
|
app:layout_constraintEnd_toStartOf="@+id/roomInvitationAccept"
|
||||||
app:layout_constraintTop_toTopOf="@+id/roomInvitationAccept" />
|
app:layout_constraintTop_toTopOf="@+id/roomInvitationAccept" />
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue