Handle room invitation actions

This commit is contained in:
Benoit Marty 2019-07-01 17:26:24 +02:00 committed by Benoit Marty
parent 07309c90e1
commit 01e3e71f98
12 changed files with 248 additions and 82 deletions

View File

@ -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>)



View File

@ -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)
} }

View File

@ -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()

} }

View File

@ -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) {

View File

@ -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>()

View File

@ -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,

View File

@ -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)
} }
} }

View File

@ -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)

View File

@ -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

View File

@ -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) }
) )
} }
} }

View File

@ -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">



View File

@ -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" />