BayernMessenger/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt

231 lines
8.1 KiB
Kotlin

/*
* 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.riotx.features.roomdirectory
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.*
import com.squareup.inject.assisted.Assisted
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.roomdirectory.PublicRoom
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsFilter
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.LiveEvent
import timber.log.Timber
private const val PUBLIC_ROOMS_LIMIT = 20
class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
private val session: Session) : VectorViewModel<PublicRoomsViewState>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: PublicRoomsViewState): RoomDirectoryViewModel
}
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, PublicRoomsViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? {
val activity: RoomDirectoryActivity = (viewModelContext as ActivityViewModelContext).activity()
return activity.roomDirectoryViewModelFactory.create(state)
}
}
private val _joinRoomErrorLiveData = MutableLiveData<LiveEvent<Throwable>>()
val joinRoomErrorLiveData: LiveData<LiveEvent<Throwable>>
get() = _joinRoomErrorLiveData
// TODO Store in ViewState?
private var currentFilter: String = ""
private var since: String? = null
private var currentTask: Cancelable? = null
// Default RoomDirectoryData
private var roomDirectoryData = RoomDirectoryData()
init {
// Load with empty filter
load()
setState {
copy(
roomDirectoryDisplayName = roomDirectoryData.displayName
)
}
// Observe joined room (from the sync)
observeJoinedRooms()
}
private fun observeJoinedRooms() {
session
.rx()
.liveRoomSummaries(fetchLastEvents = false)
.subscribe { list ->
val joinedRoomIds = list
// Keep only joined room
?.filter { it.membership == Membership.JOIN }
?.map { it.roomId }
?.toSet()
?: emptySet()
setState {
copy(
joinedRoomsIds = joinedRoomIds,
// Remove (newly) joined room id from the joining room list
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { removeAll(joinedRoomIds) },
// Remove (newly) joined room id from the joining room list in error
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { removeAll(joinedRoomIds) }
)
}
}
.disposeOnClear()
}
fun setRoomDirectoryData(roomDirectoryData: RoomDirectoryData) {
if (this.roomDirectoryData == roomDirectoryData) {
return
}
this.roomDirectoryData = roomDirectoryData
reset()
load()
}
fun filterWith(filter: String) {
if (currentFilter == filter) {
return
}
currentTask?.cancel()
currentFilter = filter
reset()
load()
}
private fun reset() {
// Reset since token
since = null
setState {
copy(
publicRooms = emptyList(),
asyncPublicRoomsRequest = Loading(),
hasMore = false,
roomDirectoryDisplayName = roomDirectoryData.displayName
)
}
}
fun loadMore() {
if (currentTask == null) {
setState {
copy(
asyncPublicRoomsRequest = Loading()
)
}
load()
}
}
private fun load() {
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
PublicRoomsParams(
limit = PUBLIC_ROOMS_LIMIT,
filter = PublicRoomsFilter(searchTerm = currentFilter),
includeAllNetworks = roomDirectoryData.includeAllNetworks,
since = since,
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
),
object : MatrixCallback<PublicRoomsResponse> {
override fun onSuccess(data: PublicRoomsResponse) {
currentTask = null
since = data.nextBatch
setState {
copy(
asyncPublicRoomsRequest = Success(data.chunk!!),
// It's ok to append at the end of the list, so I use publicRooms.size()
publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size),
hasMore = since != null
)
}
}
override fun onFailure(failure: Throwable) {
currentTask = null
setState {
copy(
asyncPublicRoomsRequest = Fail(failure)
)
}
}
})
}
fun joinRoom(publicRoom: PublicRoom) = withState { state ->
if (state.joiningRoomsIds.contains(publicRoom.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(publicRoom.roomId) }
)
}
session.joinRoom(publicRoom.roomId, 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
_joinRoomErrorLiveData.postValue(LiveEvent(failure))
setState {
copy(
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(publicRoom.roomId) },
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(publicRoom.roomId) }
)
}
}
})
}
}