mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-28 14:08:44 +03:00
Improve a bit how joining/leaving are handled
This commit is contained in:
parent
8814364497
commit
150d44aafd
39 changed files with 454 additions and 284 deletions
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.matrix.android.api.session.identity.ThreePid
|
import im.vector.matrix.android.api.session.identity.ThreePid
|
||||||
import im.vector.matrix.android.api.session.pushers.Pusher
|
import im.vector.matrix.android.api.session.pushers.Pusher
|
||||||
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
|
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
|
@ -165,6 +166,10 @@ class RxSession(private val session: Session) {
|
||||||
session.widgetService().getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes)
|
session.widgetService().getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveRoomChangeMembershipState(): Observable<Map<String, ChangeMembershipState>> {
|
||||||
|
return session.getChangeMembershipsLive().asObservable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Session.rx(): RxSession {
|
fun Session.rx(): RxSession {
|
||||||
|
|
|
@ -34,13 +34,6 @@ interface RoomDirectoryService {
|
||||||
publicRoomsParams: PublicRoomsParams,
|
publicRoomsParams: PublicRoomsParams,
|
||||||
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
|
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
|
||||||
|
|
||||||
/**
|
|
||||||
* Join a room by id, or room alias
|
|
||||||
*/
|
|
||||||
fun joinRoom(roomIdOrAlias: String,
|
|
||||||
reason: String? = null,
|
|
||||||
callback: MatrixCallback<Unit>): Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the overall metadata about protocols supported by the homeserver.
|
* Fetches the overall metadata about protocols supported by the homeserver.
|
||||||
* Includes both the available protocols and all fields required for queries against each protocol.
|
* Includes both the available protocols and all fields required for queries against each protocol.
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.room
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
@ -104,5 +105,13 @@ interface RoomService {
|
||||||
searchOnServer: Boolean,
|
searchOnServer: Boolean,
|
||||||
callback: MatrixCallback<Optional<String>>): Cancelable
|
callback: MatrixCallback<Optional<String>>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a live data of all local changes membership who happened since the session has been opened.
|
||||||
|
* It allows you to track this in your client to known what is currently being processed by the SDK.
|
||||||
|
* It won't know anything about change being done in other client.
|
||||||
|
* Keys are roomId or roomAlias, depending of what you used as parameter for the join/leave action
|
||||||
|
*/
|
||||||
|
fun getChangeMembershipsLive(): LiveData<Map<String,ChangeMembershipState>>
|
||||||
|
|
||||||
fun getExistingDirectRoomWithUser(otherUserId: String) : Room?
|
fun getExistingDirectRoomWithUser(otherUserId: String) : Room?
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ fun roomSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {
|
||||||
* [im.vector.matrix.android.api.session.room.Room] and [im.vector.matrix.android.api.session.room.RoomService]
|
* [im.vector.matrix.android.api.session.room.Room] and [im.vector.matrix.android.api.session.room.RoomService]
|
||||||
*/
|
*/
|
||||||
data class RoomSummaryQueryParams(
|
data class RoomSummaryQueryParams(
|
||||||
|
val roomId: QueryStringValue,
|
||||||
val displayName: QueryStringValue,
|
val displayName: QueryStringValue,
|
||||||
val canonicalAlias: QueryStringValue,
|
val canonicalAlias: QueryStringValue,
|
||||||
val memberships: List<Membership>
|
val memberships: List<Membership>
|
||||||
|
@ -35,11 +36,13 @@ data class RoomSummaryQueryParams(
|
||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
|
|
||||||
|
var roomId: QueryStringValue = QueryStringValue.IsNotEmpty
|
||||||
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
|
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
|
||||||
var canonicalAlias: QueryStringValue = QueryStringValue.NoCondition
|
var canonicalAlias: QueryStringValue = QueryStringValue.NoCondition
|
||||||
var memberships: List<Membership> = Membership.all()
|
var memberships: List<Membership> = Membership.all()
|
||||||
|
|
||||||
fun build() = RoomSummaryQueryParams(
|
fun build() = RoomSummaryQueryParams(
|
||||||
|
roomId = roomId,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
canonicalAlias = canonicalAlias,
|
canonicalAlias = canonicalAlias,
|
||||||
memberships = memberships
|
memberships = memberships
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.matrix.android.api.session.room.members
|
||||||
|
|
||||||
|
sealed class ChangeMembershipState() {
|
||||||
|
object Unknown : ChangeMembershipState()
|
||||||
|
object Joining : ChangeMembershipState()
|
||||||
|
data class FailedJoining(val throwable: Throwable) : ChangeMembershipState()
|
||||||
|
object Joined : ChangeMembershipState()
|
||||||
|
object Leaving : ChangeMembershipState()
|
||||||
|
data class FailedLeaving(val throwable: Throwable) : ChangeMembershipState()
|
||||||
|
object Left : ChangeMembershipState()
|
||||||
|
|
||||||
|
fun isInProgress() = this is Joining || this is Leaving
|
||||||
|
|
||||||
|
fun isSuccessful() = this is Joined || this is Left
|
||||||
|
|
||||||
|
fun isFailed() = this is FailedJoining || this is FailedLeaving
|
||||||
|
|
||||||
|
}
|
|
@ -44,14 +44,6 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun joinRoom(roomIdOrAlias: String, reason: String?, callback: MatrixCallback<Unit>): Cancelable {
|
|
||||||
return joinRoomTask
|
|
||||||
.configureWith(JoinRoomTask.Params(roomIdOrAlias, reason)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable {
|
override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable {
|
||||||
return getThirdPartyProtocolsTask
|
return getThirdPartyProtocolsTask
|
||||||
.configureWith {
|
.configureWith {
|
||||||
|
|
|
@ -21,12 +21,14 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.room.Room
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
|
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask
|
import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask
|
||||||
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
|
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
|
||||||
|
import im.vector.matrix.android.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||||
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
|
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
|
||||||
import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask
|
import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask
|
||||||
import im.vector.matrix.android.internal.session.room.summary.RoomSummaryDataSource
|
import im.vector.matrix.android.internal.session.room.summary.RoomSummaryDataSource
|
||||||
|
@ -43,6 +45,7 @@ internal class DefaultRoomService @Inject constructor(
|
||||||
private val roomIdByAliasTask: GetRoomIdByAliasTask,
|
private val roomIdByAliasTask: GetRoomIdByAliasTask,
|
||||||
private val roomGetter: RoomGetter,
|
private val roomGetter: RoomGetter,
|
||||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||||
|
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor
|
||||||
) : RoomService {
|
) : RoomService {
|
||||||
|
|
||||||
|
@ -111,4 +114,8 @@ internal class DefaultRoomService @Inject constructor(
|
||||||
}
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getChangeMembershipsLive(): LiveData<Map<String, ChangeMembershipState>> {
|
||||||
|
return roomChangeMembershipStateDataSource.getLiveStates()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.matrix.android.internal.session.room.membership
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds information about rooms that current user is joining or leaving.
|
||||||
|
*/
|
||||||
|
@SessionScope
|
||||||
|
internal class RoomChangeMembershipStateDataSource @Inject constructor() {
|
||||||
|
|
||||||
|
private val mutableLiveStates = MutableLiveData<Map<String, ChangeMembershipState>>(emptyMap())
|
||||||
|
private val states = HashMap<String, ChangeMembershipState>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will update local states to be synced with the server.
|
||||||
|
*/
|
||||||
|
fun setMembershipFromSync(roomId: String, membership: Membership) {
|
||||||
|
if (states.containsKey(roomId)) {
|
||||||
|
val newState = membership.toMembershipChangeState()
|
||||||
|
updateState(roomId, newState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateState(roomId: String, state: ChangeMembershipState) {
|
||||||
|
states[roomId] = state
|
||||||
|
mutableLiveStates.postValue(states.toMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveStates(): LiveData<Map<String, ChangeMembershipState>> {
|
||||||
|
return mutableLiveStates
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getState(roomId: String): ChangeMembershipState {
|
||||||
|
return states.getOrElse(roomId) {
|
||||||
|
ChangeMembershipState.Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Membership.toMembershipChangeState(): ChangeMembershipState {
|
||||||
|
return when {
|
||||||
|
this == Membership.JOIN -> ChangeMembershipState.Joined
|
||||||
|
this.isLeft() -> ChangeMembershipState.Left
|
||||||
|
else -> ChangeMembershipState.Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.matrix.android.internal.session.room.membership.joining
|
package im.vector.matrix.android.internal.session.room.membership.joining
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.failure.JoinRoomFailure
|
import im.vector.matrix.android.api.session.room.failure.JoinRoomFailure
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
import im.vector.matrix.android.api.session.room.model.create.JoinRoomResponse
|
import im.vector.matrix.android.api.session.room.model.create.JoinRoomResponse
|
||||||
import im.vector.matrix.android.internal.database.awaitNotEmptyResult
|
import im.vector.matrix.android.internal.database.awaitNotEmptyResult
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
|
@ -24,6 +25,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntityFields
|
||||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||||
|
import im.vector.matrix.android.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||||
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
|
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
|
@ -45,13 +47,20 @@ internal class DefaultJoinRoomTask @Inject constructor(
|
||||||
private val readMarkersTask: SetReadMarkersTask,
|
private val readMarkersTask: SetReadMarkersTask,
|
||||||
@SessionDatabase
|
@SessionDatabase
|
||||||
private val realmConfiguration: RealmConfiguration,
|
private val realmConfiguration: RealmConfiguration,
|
||||||
|
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||||
private val eventBus: EventBus
|
private val eventBus: EventBus
|
||||||
) : JoinRoomTask {
|
) : JoinRoomTask {
|
||||||
|
|
||||||
override suspend fun execute(params: JoinRoomTask.Params) {
|
override suspend fun execute(params: JoinRoomTask.Params) {
|
||||||
val joinRoomResponse = executeRequest<JoinRoomResponse>(eventBus) {
|
roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.Joining)
|
||||||
|
val joinRoomResponse = try {
|
||||||
|
executeRequest<JoinRoomResponse>(eventBus) {
|
||||||
apiCall = roomAPI.join(params.roomIdOrAlias, params.viaServers, mapOf("reason" to params.reason))
|
apiCall = roomAPI.join(params.roomIdOrAlias, params.viaServers, mapOf("reason" to params.reason))
|
||||||
}
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.FailedJoining(failure))
|
||||||
|
throw failure
|
||||||
|
}
|
||||||
// Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before)
|
// Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before)
|
||||||
val roomId = joinRoomResponse.roomId
|
val roomId = joinRoomResponse.roomId
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -19,12 +19,16 @@ package im.vector.matrix.android.internal.session.room.membership.leaving
|
||||||
import im.vector.matrix.android.api.query.QueryStringValue
|
import im.vector.matrix.android.api.query.QueryStringValue
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
import im.vector.matrix.android.api.session.room.model.create.RoomCreateContent
|
import im.vector.matrix.android.api.session.room.model.create.RoomCreateContent
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||||
|
import im.vector.matrix.android.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||||
import im.vector.matrix.android.internal.session.room.state.StateEventDataSource
|
import im.vector.matrix.android.internal.session.room.state.StateEventDataSource
|
||||||
|
import im.vector.matrix.android.internal.session.room.summary.RoomSummaryDataSource
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface LeaveRoomTask : Task<LeaveRoomTask.Params, Unit> {
|
internal interface LeaveRoomTask : Task<LeaveRoomTask.Params, Unit> {
|
||||||
|
@ -37,7 +41,9 @@ internal interface LeaveRoomTask : Task<LeaveRoomTask.Params, Unit> {
|
||||||
internal class DefaultLeaveRoomTask @Inject constructor(
|
internal class DefaultLeaveRoomTask @Inject constructor(
|
||||||
private val roomAPI: RoomAPI,
|
private val roomAPI: RoomAPI,
|
||||||
private val eventBus: EventBus,
|
private val eventBus: EventBus,
|
||||||
private val stateEventDataSource: StateEventDataSource
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
|
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||||
|
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource
|
||||||
) : LeaveRoomTask {
|
) : LeaveRoomTask {
|
||||||
|
|
||||||
override suspend fun execute(params: LeaveRoomTask.Params) {
|
override suspend fun execute(params: LeaveRoomTask.Params) {
|
||||||
|
@ -45,13 +51,29 @@ internal class DefaultLeaveRoomTask @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun leaveRoom(roomId: String, reason: String?) {
|
private suspend fun leaveRoom(roomId: String, reason: String?) {
|
||||||
val roomCreateStateEvent = stateEventDataSource.getStateEvent(roomId, eventType = EventType.STATE_ROOM_CREATE, stateKey = QueryStringValue.NoCondition)
|
val roomSummary = roomSummaryDataSource.getRoomSummary(roomId)
|
||||||
val predecessorRoomId = roomCreateStateEvent?.getClearContent()?.toModel<RoomCreateContent>()?.predecessor?.roomId
|
if (roomSummary?.membership?.isActive() == false) {
|
||||||
executeRequest<Unit>(eventBus) {
|
Timber.v("Room $roomId is not joined so can't be left")
|
||||||
apiCall = roomAPI.leave(roomId, mapOf("reason" to reason))
|
return
|
||||||
}
|
}
|
||||||
|
roomChangeMembershipStateDataSource.updateState(roomId, ChangeMembershipState.Leaving)
|
||||||
|
val roomCreateStateEvent = stateEventDataSource.getStateEvent(
|
||||||
|
roomId = roomId,
|
||||||
|
eventType = EventType.STATE_ROOM_CREATE,
|
||||||
|
stateKey = QueryStringValue.NoCondition
|
||||||
|
)
|
||||||
|
// Server is not cleaning predecessor rooms, so we also try to left them
|
||||||
|
val predecessorRoomId = roomCreateStateEvent?.getClearContent()?.toModel<RoomCreateContent>()?.predecessor?.roomId
|
||||||
if (predecessorRoomId != null) {
|
if (predecessorRoomId != null) {
|
||||||
leaveRoom(predecessorRoomId, reason)
|
leaveRoom(predecessorRoomId, reason)
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
executeRequest<Unit>(eventBus) {
|
||||||
|
apiCall = roomAPI.leave(roomId, mapOf("reason" to reason))
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
roomChangeMembershipStateDataSource.updateState(roomId, ChangeMembershipState.FailedLeaving(failure))
|
||||||
|
throw failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
||||||
|
|
||||||
private fun roomSummariesQuery(realm: Realm, queryParams: RoomSummaryQueryParams): RealmQuery<RoomSummaryEntity> {
|
private fun roomSummariesQuery(realm: Realm, queryParams: RoomSummaryQueryParams): RealmQuery<RoomSummaryEntity> {
|
||||||
val query = RoomSummaryEntity.where(realm)
|
val query = RoomSummaryEntity.where(realm)
|
||||||
|
query.process(RoomSummaryEntityFields.ROOM_ID, queryParams.roomId)
|
||||||
query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName)
|
query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName)
|
||||||
query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
|
query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
|
||||||
query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
|
query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
|
||||||
|
|
|
@ -48,6 +48,7 @@ import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
import im.vector.matrix.android.internal.session.mapWithProgress
|
import im.vector.matrix.android.internal.session.mapWithProgress
|
||||||
|
import im.vector.matrix.android.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberEventHandler
|
import im.vector.matrix.android.internal.session.room.membership.RoomMemberEventHandler
|
||||||
import im.vector.matrix.android.internal.session.room.read.FullyReadContent
|
import im.vector.matrix.android.internal.session.room.read.FullyReadContent
|
||||||
import im.vector.matrix.android.internal.session.room.summary.RoomSummaryUpdater
|
import im.vector.matrix.android.internal.session.room.summary.RoomSummaryUpdater
|
||||||
|
@ -73,6 +74,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
private val cryptoService: DefaultCryptoService,
|
private val cryptoService: DefaultCryptoService,
|
||||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||||
private val roomTypingUsersHandler: RoomTypingUsersHandler,
|
private val roomTypingUsersHandler: RoomTypingUsersHandler,
|
||||||
|
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
private val eventBus: EventBus,
|
private val eventBus: EventBus,
|
||||||
private val timelineEventDecryptor: TimelineEventDecryptor) {
|
private val timelineEventDecryptor: TimelineEventDecryptor) {
|
||||||
|
@ -185,6 +187,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
} != null
|
} != null
|
||||||
|
|
||||||
roomTypingUsersHandler.handle(realm, roomId, ephemeralResult)
|
roomTypingUsersHandler.handle(realm, roomId, ephemeralResult)
|
||||||
|
roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.JOIN)
|
||||||
roomSummaryUpdater.update(
|
roomSummaryUpdater.update(
|
||||||
realm,
|
realm,
|
||||||
roomId,
|
roomId,
|
||||||
|
@ -221,6 +224,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
val inviterEvent = roomSync.inviteState?.events?.lastOrNull {
|
val inviterEvent = roomSync.inviteState?.events?.lastOrNull {
|
||||||
it.type == EventType.STATE_ROOM_MEMBER
|
it.type == EventType.STATE_ROOM_MEMBER
|
||||||
}
|
}
|
||||||
|
roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.INVITE)
|
||||||
roomSummaryUpdater.update(realm, roomId, Membership.INVITE, updateMembers = true, inviterId = inviterEvent?.senderId)
|
roomSummaryUpdater.update(realm, roomId, Membership.INVITE, updateMembers = true, inviterId = inviterEvent?.senderId)
|
||||||
return roomEntity
|
return roomEntity
|
||||||
}
|
}
|
||||||
|
@ -263,6 +267,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
val membership = leftMember?.membership ?: Membership.LEAVE
|
val membership = leftMember?.membership ?: Membership.LEAVE
|
||||||
roomEntity.membership = membership
|
roomEntity.membership = membership
|
||||||
roomEntity.chunks.deleteAllFromRealm()
|
roomEntity.chunks.deleteAllFromRealm()
|
||||||
|
roomTypingUsersHandler.handle(realm, roomId, null)
|
||||||
|
roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.LEAVE)
|
||||||
roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications)
|
roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications)
|
||||||
return roomEntity
|
return roomEntity
|
||||||
}
|
}
|
||||||
|
|
|
@ -883,7 +883,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
} else if (summary?.membership == Membership.INVITE && inviter != null) {
|
} else if (summary?.membership == Membership.INVITE && inviter != null) {
|
||||||
roomToolbarContentView.isClickable = false
|
roomToolbarContentView.isClickable = false
|
||||||
inviteView.visibility = View.VISIBLE
|
inviteView.visibility = View.VISIBLE
|
||||||
inviteView.render(inviter, VectorInviteView.Mode.LARGE)
|
inviteView.render(inviter, VectorInviteView.Mode.LARGE, state.changeMembershipState)
|
||||||
// Intercept click event
|
// Intercept click event
|
||||||
inviteView.setOnClickListener { }
|
inviteView.setOnClickListener { }
|
||||||
} else if (state.asyncInviter.complete) {
|
} else if (state.asyncInviter.complete) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.file.FileService
|
import im.vector.matrix.android.api.session.file.FileService
|
||||||
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
|
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
|
import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
|
||||||
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.PowerLevelsContent
|
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
|
||||||
|
@ -166,6 +167,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
timeline.start()
|
timeline.start()
|
||||||
timeline.addListener(this)
|
timeline.addListener(this)
|
||||||
observeRoomSummary()
|
observeRoomSummary()
|
||||||
|
observeMembershipChanges()
|
||||||
observeSummaryState()
|
observeSummaryState()
|
||||||
getUnreadState()
|
getUnreadState()
|
||||||
observeSyncState()
|
observeSyncState()
|
||||||
|
@ -629,7 +631,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
||||||
session.joinRoom(command.roomAlias, command.reason, object : MatrixCallback<Unit> {
|
session.joinRoom(command.roomAlias, command.reason, emptyList(), object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
session.getRoomSummary(command.roomAlias)
|
session.getRoomSummary(command.roomAlias)
|
||||||
?.roomId
|
?.roomId
|
||||||
|
@ -1147,6 +1149,19 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observeMembershipChanges() {
|
||||||
|
session.rx()
|
||||||
|
.liveRoomChangeMembershipState()
|
||||||
|
.map {
|
||||||
|
it[initialState.roomId] ?: ChangeMembershipState.Unknown
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.subscribe {
|
||||||
|
setState { copy(changeMembershipState = it) }
|
||||||
|
}
|
||||||
|
.disposeOnClear()
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeSummaryState() {
|
private fun observeSummaryState() {
|
||||||
asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary ->
|
asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary ->
|
||||||
roomSummaryHolder.set(summary)
|
roomSummaryHolder.set(summary)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
|
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
|
||||||
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.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
@ -64,6 +65,7 @@ data class RoomDetailViewState(
|
||||||
val highlightedEventId: String? = null,
|
val highlightedEventId: String? = null,
|
||||||
val unreadState: UnreadState = UnreadState.Unknown,
|
val unreadState: UnreadState = UnreadState.Unknown,
|
||||||
val canShowJumpToReadMarker: Boolean = true,
|
val canShowJumpToReadMarker: Boolean = true,
|
||||||
|
val changeMembershipState: ChangeMembershipState = ChangeMembershipState.Unknown,
|
||||||
val canSendMessage: Boolean = true
|
val canSendMessage: Boolean = true
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,9 @@ package im.vector.riotx.features.home.room.list
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
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.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
import im.vector.matrix.android.api.util.MatrixItem
|
import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
|
@ -29,6 +29,7 @@ import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.riotx.core.extensions.setTextOrHide
|
import im.vector.riotx.core.extensions.setTextOrHide
|
||||||
import im.vector.riotx.core.platform.ButtonStateView
|
import im.vector.riotx.core.platform.ButtonStateView
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
|
import im.vector.riotx.features.invite.InviteButtonStateBinder
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_room_invitation)
|
@EpoxyModelClass(layout = R.layout.item_room_invitation)
|
||||||
abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>() {
|
abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>() {
|
||||||
|
@ -37,53 +38,36 @@ abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>(
|
||||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||||
@EpoxyAttribute var secondLine: CharSequence? = null
|
@EpoxyAttribute var secondLine: CharSequence? = null
|
||||||
@EpoxyAttribute var listener: (() -> Unit)? = null
|
@EpoxyAttribute var listener: (() -> Unit)? = null
|
||||||
@EpoxyAttribute var invitationAcceptInProgress: Boolean = false
|
@EpoxyAttribute lateinit var changeMembershipState: ChangeMembershipState
|
||||||
@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
|
||||||
|
|
||||||
|
private val acceptCallback = object : ButtonStateView.Callback {
|
||||||
|
override fun onButtonClicked() {
|
||||||
|
acceptListener?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRetryClicked() {
|
||||||
|
acceptListener?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val rejectCallback = object : ButtonStateView.Callback {
|
||||||
|
override fun onButtonClicked() {
|
||||||
|
rejectListener?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRetryClicked() {
|
||||||
|
rejectListener?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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.callback = acceptCallback
|
||||||
// When a request is in progress (accept or reject), we only use the accept State button
|
holder.rejectView.callback = rejectCallback
|
||||||
val requestInProgress = invitationAcceptInProgress || invitationRejectInProgress
|
InviteButtonStateBinder.bind(holder.acceptView, holder.rejectView, changeMembershipState)
|
||||||
|
|
||||||
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 = matrixItem.getBestName()
|
holder.titleView.text = matrixItem.getBestName()
|
||||||
holder.subtitleView.setTextOrHide(secondLine)
|
holder.subtitleView.setTextOrHide(secondLine)
|
||||||
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||||
|
|
|
@ -21,10 +21,12 @@ import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.NoOpMatrixCallback
|
import im.vector.matrix.android.api.NoOpMatrixCallback
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
import im.vector.matrix.android.api.session.Session
|
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.matrix.rx.rx
|
||||||
import im.vector.riotx.core.extensions.exhaustive
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.utils.DataSource
|
import im.vector.riotx.core.utils.DataSource
|
||||||
|
@ -55,6 +57,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeRoomSummaries()
|
observeRoomSummaries()
|
||||||
|
observeMembershipChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: RoomListAction) {
|
override fun handle(action: RoomListAction) {
|
||||||
|
@ -102,37 +105,19 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
||||||
.observeOn(Schedulers.computation())
|
.observeOn(Schedulers.computation())
|
||||||
.map { buildRoomSummaries(it) }
|
.map { buildRoomSummaries(it) }
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
val invitedRooms = async()?.get(RoomCategory.INVITE)?.map { it.roomId }.orEmpty()
|
copy(asyncFilteredRooms = async)
|
||||||
val remainingJoining = joiningRoomsIds.intersect(invitedRooms)
|
|
||||||
val remainingJoinErrors = joiningErrorRoomsIds.intersect(invitedRooms)
|
|
||||||
val remainingRejecting = rejectingRoomsIds.intersect(invitedRooms)
|
|
||||||
val remainingRejectErrors = rejectingErrorRoomsIds.intersect(invitedRooms)
|
|
||||||
copy(
|
|
||||||
asyncFilteredRooms = async,
|
|
||||||
joiningRoomsIds = remainingJoining,
|
|
||||||
joiningErrorRoomsIds = remainingJoinErrors,
|
|
||||||
rejectingRoomsIds = remainingRejecting,
|
|
||||||
rejectingErrorRoomsIds = remainingRejectErrors
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAcceptInvitation(action: RoomListAction.AcceptInvitation) = withState { state ->
|
private fun handleAcceptInvitation(action: RoomListAction.AcceptInvitation) = withState { state ->
|
||||||
val roomId = action.roomSummary.roomId
|
val roomId = action.roomSummary.roomId
|
||||||
|
val roomMembershipChange = state.roomMembershipChanges[roomId]
|
||||||
if (state.joiningRoomsIds.contains(roomId) || state.rejectingRoomsIds.contains(roomId)) {
|
if (roomMembershipChange?.isInProgress().orFalse()) {
|
||||||
// Request already sent, should not happen
|
// Request already sent, should not happen
|
||||||
Timber.w("Try to join an already joining room. Should not happen")
|
Timber.w("Try to join an already joining room. Should not happen")
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
joiningRoomsIds = joiningRoomsIds + roomId,
|
|
||||||
rejectingErrorRoomsIds = rejectingErrorRoomsIds - roomId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getRoom(roomId)?.join(callback = object : MatrixCallback<Unit> {
|
session.getRoom(roomId)?.join(callback = object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||||
|
@ -142,32 +127,19 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
// Notify the user
|
// Notify the user
|
||||||
_viewEvents.post(RoomListViewEvents.Failure(failure))
|
_viewEvents.post(RoomListViewEvents.Failure(failure))
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
joiningRoomsIds = joiningRoomsIds - roomId,
|
|
||||||
joiningErrorRoomsIds = joiningErrorRoomsIds + roomId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRejectInvitation(action: RoomListAction.RejectInvitation) = withState { state ->
|
private fun handleRejectInvitation(action: RoomListAction.RejectInvitation) = withState { state ->
|
||||||
val roomId = action.roomSummary.roomId
|
val roomId = action.roomSummary.roomId
|
||||||
|
val roomMembershipChange = state.roomMembershipChanges[roomId]
|
||||||
if (state.joiningRoomsIds.contains(roomId) || state.rejectingRoomsIds.contains(roomId)) {
|
if (roomMembershipChange?.isInProgress().orFalse()) {
|
||||||
// Request already sent, should not happen
|
// Request already sent, should not happen
|
||||||
Timber.w("Try to reject an already rejecting room. Should not happen")
|
Timber.w("Try to left an already leaving or joining room. Should not happen")
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
rejectingRoomsIds = rejectingRoomsIds + roomId,
|
|
||||||
joiningErrorRoomsIds = joiningErrorRoomsIds - roomId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getRoom(roomId)?.leave(null, object : MatrixCallback<Unit> {
|
session.getRoom(roomId)?.leave(null, object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the rejectingRoomsIds here, because, the room is not rejected yet regarding the sync data.
|
// We do not update the rejectingRoomsIds here, because, the room is not rejected yet regarding the sync data.
|
||||||
|
@ -179,12 +151,6 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
// Notify the user
|
// Notify the user
|
||||||
_viewEvents.post(RoomListViewEvents.Failure(failure))
|
_viewEvents.post(RoomListViewEvents.Failure(failure))
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
rejectingRoomsIds = rejectingRoomsIds - roomId,
|
|
||||||
rejectingErrorRoomsIds = rejectingErrorRoomsIds + roomId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -235,6 +201,16 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observeMembershipChanges() {
|
||||||
|
session.rx()
|
||||||
|
.liveRoomChangeMembershipState()
|
||||||
|
.subscribe {
|
||||||
|
Timber.v("ChangeMembership states: $it")
|
||||||
|
setState { copy(roomMembershipChanges = it) }
|
||||||
|
}
|
||||||
|
.disposeOnClear()
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
|
private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
|
||||||
// Set up init size on directChats and groupRooms as they are the biggest ones
|
// Set up init size on directChats and groupRooms as they are the biggest ones
|
||||||
val invites = ArrayList<RoomSummary>()
|
val invites = ArrayList<RoomSummary>()
|
||||||
|
|
|
@ -20,6 +20,7 @@ import androidx.annotation.StringRes
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -30,14 +31,7 @@ data class RoomListViewState(
|
||||||
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
||||||
val roomFilter: String = "",
|
val roomFilter: String = "",
|
||||||
val asyncFilteredRooms: Async<RoomSummaries> = Uninitialized,
|
val asyncFilteredRooms: Async<RoomSummaries> = Uninitialized,
|
||||||
// List of roomIds that the user wants to join
|
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap(),
|
||||||
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,
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.list
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import com.airbnb.epoxy.EpoxyController
|
import com.airbnb.epoxy.EpoxyController
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -72,10 +73,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
.filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
|
.filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
|
||||||
|
|
||||||
buildRoomModels(filteredSummaries,
|
buildRoomModels(filteredSummaries,
|
||||||
viewState.joiningRoomsIds,
|
viewState.roomMembershipChanges,
|
||||||
viewState.joiningErrorRoomsIds,
|
|
||||||
viewState.rejectingRoomsIds,
|
|
||||||
viewState.rejectingErrorRoomsIds,
|
|
||||||
emptySet())
|
emptySet())
|
||||||
|
|
||||||
addFilterFooter(viewState)
|
addFilterFooter(viewState)
|
||||||
|
@ -94,10 +92,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
}
|
}
|
||||||
if (isExpanded) {
|
if (isExpanded) {
|
||||||
buildRoomModels(summaries,
|
buildRoomModels(summaries,
|
||||||
viewState.joiningRoomsIds,
|
viewState.roomMembershipChanges,
|
||||||
viewState.joiningErrorRoomsIds,
|
|
||||||
viewState.rejectingRoomsIds,
|
|
||||||
viewState.rejectingErrorRoomsIds,
|
|
||||||
emptySet())
|
emptySet())
|
||||||
// Never set showHelp to true for invitation
|
// Never set showHelp to true for invitation
|
||||||
if (category != RoomCategory.INVITE) {
|
if (category != RoomCategory.INVITE) {
|
||||||
|
@ -153,18 +148,12 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildRoomModels(summaries: List<RoomSummary>,
|
private fun buildRoomModels(summaries: List<RoomSummary>,
|
||||||
joiningRoomsIds: Set<String>,
|
roomChangedMembershipStates: Map<String, ChangeMembershipState>,
|
||||||
joiningErrorRoomsIds: Set<String>,
|
|
||||||
rejectingRoomsIds: Set<String>,
|
|
||||||
rejectingErrorRoomsIds: Set<String>,
|
|
||||||
selectedRoomIds: Set<String>) {
|
selectedRoomIds: Set<String>) {
|
||||||
summaries.forEach { roomSummary ->
|
summaries.forEach { roomSummary ->
|
||||||
roomSummaryItemFactory
|
roomSummaryItemFactory
|
||||||
.create(roomSummary,
|
.create(roomSummary,
|
||||||
joiningRoomsIds,
|
roomChangedMembershipStates,
|
||||||
joiningErrorRoomsIds,
|
|
||||||
rejectingRoomsIds,
|
|
||||||
rejectingErrorRoomsIds,
|
|
||||||
selectedRoomIds,
|
selectedRoomIds,
|
||||||
listener)
|
listener)
|
||||||
.addTo(this)
|
.addTo(this)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.riotx.features.home.room.list
|
package im.vector.riotx.features.home.room.list
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
|
@ -39,23 +40,20 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
||||||
private val avatarRenderer: AvatarRenderer) {
|
private val avatarRenderer: AvatarRenderer) {
|
||||||
|
|
||||||
fun create(roomSummary: RoomSummary,
|
fun create(roomSummary: RoomSummary,
|
||||||
joiningRoomsIds: Set<String>,
|
roomChangeMembershipStates: Map<String, ChangeMembershipState>,
|
||||||
joiningErrorRoomsIds: Set<String>,
|
|
||||||
rejectingRoomsIds: Set<String>,
|
|
||||||
rejectingErrorRoomsIds: Set<String>,
|
|
||||||
selectedRoomIds: Set<String>,
|
selectedRoomIds: Set<String>,
|
||||||
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
||||||
return when (roomSummary.membership) {
|
return when (roomSummary.membership) {
|
||||||
Membership.INVITE -> createInvitationItem(roomSummary, joiningRoomsIds, joiningErrorRoomsIds, rejectingRoomsIds, rejectingErrorRoomsIds, listener)
|
Membership.INVITE -> {
|
||||||
|
val changeMembershipState = roomChangeMembershipStates[roomSummary.roomId] ?: ChangeMembershipState.Unknown
|
||||||
|
createInvitationItem(roomSummary, changeMembershipState, listener)
|
||||||
|
}
|
||||||
else -> createRoomItem(roomSummary, selectedRoomIds, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked })
|
else -> createRoomItem(roomSummary, selectedRoomIds, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createInvitationItem(roomSummary: RoomSummary,
|
private fun createInvitationItem(roomSummary: RoomSummary,
|
||||||
joiningRoomsIds: Set<String>,
|
changeMembershipState: ChangeMembershipState,
|
||||||
joiningErrorRoomsIds: Set<String>,
|
|
||||||
rejectingRoomsIds: Set<String>,
|
|
||||||
rejectingErrorRoomsIds: Set<String>,
|
|
||||||
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
||||||
val secondLine = if (roomSummary.isDirect) {
|
val secondLine = if (roomSummary.isDirect) {
|
||||||
roomSummary.inviterId
|
roomSummary.inviterId
|
||||||
|
@ -70,10 +68,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
||||||
.avatarRenderer(avatarRenderer)
|
.avatarRenderer(avatarRenderer)
|
||||||
.matrixItem(roomSummary.toMatrixItem())
|
.matrixItem(roomSummary.toMatrixItem())
|
||||||
.secondLine(secondLine)
|
.secondLine(secondLine)
|
||||||
.invitationAcceptInProgress(joiningRoomsIds.contains(roomSummary.roomId))
|
.changeMembershipState(changeMembershipState)
|
||||||
.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) }
|
||||||
.listener { listener?.onRoomClicked(roomSummary) }
|
.listener { listener?.onRoomClicked(roomSummary) }
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.invite
|
||||||
|
|
||||||
|
import androidx.core.view.isInvisible
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
|
import im.vector.riotx.core.platform.ButtonStateView
|
||||||
|
|
||||||
|
object InviteButtonStateBinder {
|
||||||
|
|
||||||
|
fun bind(
|
||||||
|
acceptView: ButtonStateView,
|
||||||
|
rejectView: ButtonStateView,
|
||||||
|
changeMembershipState: ChangeMembershipState
|
||||||
|
) {
|
||||||
|
// When a request is in progress (accept or reject), we only use the accept State button
|
||||||
|
// We check for isSuccessful, otherwise we get a glitch the time room summaries get rebuilt
|
||||||
|
|
||||||
|
val requestInProgress = changeMembershipState.isInProgress() || changeMembershipState.isSuccessful()
|
||||||
|
when {
|
||||||
|
requestInProgress -> acceptView.render(ButtonStateView.State.Loading)
|
||||||
|
changeMembershipState is ChangeMembershipState.FailedJoining -> acceptView.render(ButtonStateView.State.Error)
|
||||||
|
else -> acceptView.render(ButtonStateView.State.Button)
|
||||||
|
}
|
||||||
|
// ButtonStateView.State.Loaded not used because roomSummary will not be displayed as a room invitation anymore
|
||||||
|
|
||||||
|
rejectView.isInvisible = requestInProgress
|
||||||
|
|
||||||
|
when {
|
||||||
|
changeMembershipState is ChangeMembershipState.FailedLeaving -> rejectView.render(ButtonStateView.State.Error)
|
||||||
|
else -> rejectView.render(ButtonStateView.State.Button)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,10 +21,12 @@ import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.HasScreenInjector
|
import im.vector.riotx.core.di.HasScreenInjector
|
||||||
|
import im.vector.riotx.core.platform.ButtonStateView
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import kotlinx.android.synthetic.main.vector_invite_view.view.*
|
import kotlinx.android.synthetic.main.vector_invite_view.view.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -50,11 +52,28 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||||
context.injector().inject(this)
|
context.injector().inject(this)
|
||||||
}
|
}
|
||||||
View.inflate(context, R.layout.vector_invite_view, this)
|
View.inflate(context, R.layout.vector_invite_view, this)
|
||||||
inviteRejectView.setOnClickListener { callback?.onRejectInvite() }
|
inviteAcceptView.callback = object : ButtonStateView.Callback {
|
||||||
inviteAcceptView.setOnClickListener { callback?.onAcceptInvite() }
|
override fun onButtonClicked() {
|
||||||
|
callback?.onAcceptInvite()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(sender: User, mode: Mode = Mode.LARGE) {
|
override fun onRetryClicked() {
|
||||||
|
callback?.onAcceptInvite()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inviteRejectView.callback = object : ButtonStateView.Callback {
|
||||||
|
override fun onButtonClicked() {
|
||||||
|
callback?.onRejectInvite()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRetryClicked() {
|
||||||
|
callback?.onRejectInvite()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun render(sender: User, mode: Mode = Mode.LARGE, changeMembershipState: ChangeMembershipState) {
|
||||||
if (mode == Mode.LARGE) {
|
if (mode == Mode.LARGE) {
|
||||||
updateLayoutParams { height = LayoutParams.MATCH_CONSTRAINT }
|
updateLayoutParams { height = LayoutParams.MATCH_CONSTRAINT }
|
||||||
avatarRenderer.render(sender.toMatrixItem(), inviteAvatarView)
|
avatarRenderer.render(sender.toMatrixItem(), inviteAvatarView)
|
||||||
|
@ -68,5 +87,6 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||||
inviteNameView.visibility = View.GONE
|
inviteNameView.visibility = View.GONE
|
||||||
inviteLabelView.text = context.getString(R.string.invited_by, sender.userId)
|
inviteLabelView.text = context.getString(R.string.invited_by, sender.userId)
|
||||||
}
|
}
|
||||||
|
InviteButtonStateBinder.bind(inviteAcceptView, inviteRejectView, changeMembershipState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||||
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.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
||||||
import im.vector.matrix.android.api.session.terms.TermsService
|
import im.vector.matrix.android.api.session.terms.TermsService
|
||||||
import im.vector.matrix.android.api.session.widgets.model.Widget
|
import im.vector.matrix.android.api.session.widgets.model.Widget
|
||||||
import im.vector.matrix.android.api.util.MatrixItem
|
import im.vector.matrix.android.api.util.MatrixItem
|
||||||
|
@ -159,8 +160,8 @@ class DefaultNavigator @Inject constructor(
|
||||||
activity.finish()
|
activity.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openRoomPreview(publicRoom: PublicRoom, context: Context) {
|
override fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) {
|
||||||
val intent = RoomPreviewActivity.getIntent(context, publicRoom)
|
val intent = RoomPreviewActivity.getIntent(context, publicRoom, roomDirectoryData)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.view.View
|
||||||
import androidx.core.util.Pair
|
import androidx.core.util.Pair
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
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.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
||||||
import im.vector.matrix.android.api.session.terms.TermsService
|
import im.vector.matrix.android.api.session.terms.TermsService
|
||||||
import im.vector.matrix.android.api.util.MatrixItem
|
import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.matrix.android.api.session.widgets.model.Widget
|
import im.vector.matrix.android.api.session.widgets.model.Widget
|
||||||
|
@ -48,7 +49,7 @@ interface Navigator {
|
||||||
|
|
||||||
fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String? = null, buildTask: Boolean = false)
|
fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String? = null, buildTask: Boolean = false)
|
||||||
|
|
||||||
fun openRoomPreview(publicRoom: PublicRoom, context: Context)
|
fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData)
|
||||||
|
|
||||||
fun openCreateRoom(context: Context, initialName: String = "")
|
fun openCreateRoom(context: Context, initialName: String = "")
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.airbnb.epoxy.VisibilityState
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Incomplete
|
import com.airbnb.mvrx.Incomplete
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -89,13 +90,14 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
|
||||||
roomTopic(publicRoom.topic)
|
roomTopic(publicRoom.topic)
|
||||||
nbOfMembers(publicRoom.numJoinedMembers)
|
nbOfMembers(publicRoom.numJoinedMembers)
|
||||||
|
|
||||||
|
val roomChangeMembership = viewState.changeMembershipStates[publicRoom.roomId] ?: ChangeMembershipState.Unknown
|
||||||
|
val isJoined = viewState.joinedRoomsIds.contains(publicRoom.roomId) || roomChangeMembership is ChangeMembershipState.Joined
|
||||||
val joinState = when {
|
val joinState = when {
|
||||||
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> JoinState.JOINED
|
isJoined -> JoinState.JOINED
|
||||||
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> JoinState.JOINING
|
roomChangeMembership is ChangeMembershipState.Joining -> JoinState.JOINING
|
||||||
viewState.joiningErrorRoomsIds.contains(publicRoom.roomId) -> JoinState.JOINING_ERROR
|
roomChangeMembership is ChangeMembershipState.FailedJoining -> JoinState.JOINING_ERROR
|
||||||
else -> JoinState.NOT_JOINED
|
else -> JoinState.NOT_JOINED
|
||||||
}
|
}
|
||||||
|
|
||||||
joinState(joinState)
|
joinState(joinState)
|
||||||
|
|
||||||
joinListener {
|
joinListener {
|
||||||
|
|
|
@ -114,26 +114,22 @@ class PublicRoomsFragment @Inject constructor(
|
||||||
|
|
||||||
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) {
|
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) {
|
||||||
Timber.v("PublicRoomClicked: $publicRoom")
|
Timber.v("PublicRoomClicked: $publicRoom")
|
||||||
|
withState(viewModel) { state ->
|
||||||
when (joinState) {
|
when (joinState) {
|
||||||
JoinState.JOINED -> {
|
JoinState.JOINED -> {
|
||||||
navigator.openRoom(requireActivity(), publicRoom.roomId)
|
navigator.openRoom(requireActivity(), publicRoom.roomId)
|
||||||
}
|
}
|
||||||
JoinState.NOT_JOINED,
|
|
||||||
JoinState.JOINING_ERROR -> {
|
|
||||||
// ROOM PREVIEW
|
|
||||||
navigator.openRoomPreview(publicRoom, requireActivity())
|
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
Snackbar.make(publicRoomsCoordinator, getString(R.string.please_wait), Snackbar.LENGTH_SHORT)
|
// ROOM PREVIEW
|
||||||
.show()
|
navigator.openRoomPreview(requireActivity(), publicRoom, state.roomDirectoryData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPublicRoomJoin(publicRoom: PublicRoom) {
|
override fun onPublicRoomJoin(publicRoom: PublicRoom) {
|
||||||
Timber.v("PublicRoomJoinClicked: $publicRoom")
|
Timber.v("PublicRoomJoinClicked: $publicRoom")
|
||||||
viewModel.handle(RoomDirectoryAction.JoinRoom(publicRoom.getPrimaryAlias(), publicRoom.roomId))
|
viewModel.handle(RoomDirectoryAction.JoinRoom(publicRoom.roomId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadMore() {
|
override fun loadMore() {
|
||||||
|
|
|
@ -19,7 +19,9 @@ package im.vector.riotx.features.roomdirectory
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
||||||
|
|
||||||
data class PublicRoomsViewState(
|
data class PublicRoomsViewState(
|
||||||
// The current filter
|
// The current filter
|
||||||
|
@ -30,11 +32,9 @@ 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,
|
||||||
// Set of roomIds that the user wants to join
|
|
||||||
val joiningRoomsIds: Set<String> = emptySet(),
|
|
||||||
// Set of roomIds that the user wants to join, but an error occurred
|
|
||||||
val joiningErrorRoomsIds: Set<String> = emptySet(),
|
|
||||||
// Set of joined roomId,
|
// Set of joined roomId,
|
||||||
val joinedRoomsIds: Set<String> = emptySet(),
|
val joinedRoomsIds: Set<String> = emptySet(),
|
||||||
val roomDirectoryDisplayName: String? = null
|
// keys are room alias or roomId
|
||||||
|
val changeMembershipStates: Map<String, ChangeMembershipState> = emptyMap(),
|
||||||
|
val roomDirectoryData: RoomDirectoryData = RoomDirectoryData()
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
|
|
@ -23,5 +23,5 @@ sealed class RoomDirectoryAction : VectorViewModelAction {
|
||||||
data class SetRoomDirectoryData(val roomDirectoryData: RoomDirectoryData) : RoomDirectoryAction()
|
data class SetRoomDirectoryData(val roomDirectoryData: RoomDirectoryData) : RoomDirectoryAction()
|
||||||
data class FilterWith(val filter: String) : RoomDirectoryAction()
|
data class FilterWith(val filter: String) : RoomDirectoryAction()
|
||||||
object LoadMore : RoomDirectoryAction()
|
object LoadMore : RoomDirectoryAction()
|
||||||
data class JoinRoom(val roomAlias: String?, val roomId: String) : RoomDirectoryAction()
|
data class JoinRoom(val roomId: String) : RoomDirectoryAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ 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
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.Session
|
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
|
||||||
|
@ -63,18 +64,10 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
|
|
||||||
private var currentTask: Cancelable? = null
|
private var currentTask: Cancelable? = null
|
||||||
|
|
||||||
// Default RoomDirectoryData
|
|
||||||
private var roomDirectoryData = RoomDirectoryData()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
roomDirectoryDisplayName = roomDirectoryData.displayName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Observe joined room (from the sync)
|
// Observe joined room (from the sync)
|
||||||
observeJoinedRooms()
|
observeJoinedRooms()
|
||||||
|
observeMembershipChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeJoinedRooms() {
|
private fun observeJoinedRooms() {
|
||||||
|
@ -91,18 +84,21 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
?: emptySet()
|
?: emptySet()
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(joinedRoomsIds = joinedRoomIds)
|
||||||
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()
|
.disposeOnClear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observeMembershipChanges() {
|
||||||
|
session.rx()
|
||||||
|
.liveRoomChangeMembershipState()
|
||||||
|
.subscribe {
|
||||||
|
setState { copy(changeMembershipStates = it) }
|
||||||
|
}
|
||||||
|
.disposeOnClear()
|
||||||
|
}
|
||||||
|
|
||||||
override fun handle(action: RoomDirectoryAction) {
|
override fun handle(action: RoomDirectoryAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomDirectoryAction.SetRoomDirectoryData -> setRoomDirectoryData(action)
|
is RoomDirectoryAction.SetRoomDirectoryData -> setRoomDirectoryData(action)
|
||||||
|
@ -112,15 +108,15 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setRoomDirectoryData(action: RoomDirectoryAction.SetRoomDirectoryData) {
|
private fun setRoomDirectoryData(action: RoomDirectoryAction.SetRoomDirectoryData) = withState {
|
||||||
if (this.roomDirectoryData == action.roomDirectoryData) {
|
if (it.roomDirectoryData == action.roomDirectoryData) {
|
||||||
return
|
return@withState
|
||||||
|
}
|
||||||
|
setState{
|
||||||
|
copy(roomDirectoryData = action.roomDirectoryData)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.roomDirectoryData = action.roomDirectoryData
|
|
||||||
|
|
||||||
reset("")
|
reset("")
|
||||||
load("")
|
load("", action.roomDirectoryData)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun filterWith(action: RoomDirectoryAction.FilterWith) = withState { state ->
|
private fun filterWith(action: RoomDirectoryAction.FilterWith) = withState { state ->
|
||||||
|
@ -128,7 +124,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
currentTask?.cancel()
|
currentTask?.cancel()
|
||||||
|
|
||||||
reset(action.filter)
|
reset(action.filter)
|
||||||
load(action.filter)
|
load(action.filter, state.roomDirectoryData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +137,6 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
publicRooms = emptyList(),
|
publicRooms = emptyList(),
|
||||||
asyncPublicRoomsRequest = Loading(),
|
asyncPublicRoomsRequest = Loading(),
|
||||||
hasMore = false,
|
hasMore = false,
|
||||||
roomDirectoryDisplayName = roomDirectoryData.displayName,
|
|
||||||
currentFilter = newFilter
|
currentFilter = newFilter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -154,12 +149,11 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
asyncPublicRoomsRequest = Loading()
|
asyncPublicRoomsRequest = Loading()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
load(state.currentFilter, state.roomDirectoryData)
|
||||||
load(state.currentFilter)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun load(filter: String) {
|
private fun load(filter: String, roomDirectoryData: RoomDirectoryData) {
|
||||||
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
||||||
PublicRoomsParams(
|
PublicRoomsParams(
|
||||||
limit = PUBLIC_ROOMS_LIMIT,
|
limit = PUBLIC_ROOMS_LIMIT,
|
||||||
|
@ -204,19 +198,16 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun joinRoom(action: RoomDirectoryAction.JoinRoom) = withState { state ->
|
private fun joinRoom(action: RoomDirectoryAction.JoinRoom) = withState { state ->
|
||||||
if (state.joiningRoomsIds.contains(action.roomId)) {
|
val roomMembershipChange = state.changeMembershipStates[action.roomId]
|
||||||
|
if (roomMembershipChange?.isInProgress().orFalse()) {
|
||||||
// Request already sent, should not happen
|
// Request already sent, should not happen
|
||||||
Timber.w("Try to join an already joining room. Should not happen")
|
Timber.w("Try to join an already joining room. Should not happen")
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
val viaServers = state.roomDirectoryData.homeServer?.let {
|
||||||
setState {
|
listOf(it)
|
||||||
copy(
|
} ?: emptyList()
|
||||||
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { add(action.roomId) }
|
session.joinRoom(action.roomId, viaServers = viaServers, callback = object : MatrixCallback<Unit> {
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
session.joinRoom(action.roomAlias ?: action.roomId, callback = object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// 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
|
// Instead, we wait for the room to be joined
|
||||||
|
@ -225,20 +216,12 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
// Notify the user
|
// Notify the user
|
||||||
_viewEvents.post(RoomDirectoryViewEvents.Failure(failure))
|
_viewEvents.post(RoomDirectoryViewEvents.Failure(failure))
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(action.roomId) },
|
|
||||||
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(action.roomId) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
|
|
||||||
currentTask?.cancel()
|
currentTask?.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,5 @@ package im.vector.riotx.features.roomdirectory.roompreview
|
||||||
import im.vector.riotx.core.platform.VectorViewModelAction
|
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||||
|
|
||||||
sealed class RoomPreviewAction : VectorViewModelAction {
|
sealed class RoomPreviewAction : VectorViewModelAction {
|
||||||
data class Join(val roomAlias: String?) : RoomPreviewAction()
|
object Join : RoomPreviewAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.content.Intent
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
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.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
||||||
import im.vector.matrix.android.api.util.MatrixItem
|
import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.addFragment
|
import im.vector.riotx.core.extensions.addFragment
|
||||||
|
@ -35,7 +36,8 @@ data class RoomPreviewData(
|
||||||
val roomAlias: String?,
|
val roomAlias: String?,
|
||||||
val topic: String?,
|
val topic: String?,
|
||||||
val worldReadable: Boolean,
|
val worldReadable: Boolean,
|
||||||
val avatarUrl: String?
|
val avatarUrl: String?,
|
||||||
|
val homeServer: String?
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val matrixItem: MatrixItem
|
val matrixItem: MatrixItem
|
||||||
get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl)
|
get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl)
|
||||||
|
@ -46,7 +48,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG = "ARG"
|
private const val ARG = "ARG"
|
||||||
|
|
||||||
fun getIntent(context: Context, publicRoom: PublicRoom): Intent {
|
fun getIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent {
|
||||||
return Intent(context, RoomPreviewActivity::class.java).apply {
|
return Intent(context, RoomPreviewActivity::class.java).apply {
|
||||||
putExtra(ARG, RoomPreviewData(
|
putExtra(ARG, RoomPreviewData(
|
||||||
roomId = publicRoom.roomId,
|
roomId = publicRoom.roomId,
|
||||||
|
@ -54,7 +56,8 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
roomAlias = publicRoom.getPrimaryAlias(),
|
roomAlias = publicRoom.getPrimaryAlias(),
|
||||||
topic = publicRoom.topic,
|
topic = publicRoom.topic,
|
||||||
worldReadable = publicRoom.worldReadable,
|
worldReadable = publicRoom.worldReadable,
|
||||||
avatarUrl = publicRoom.avatarUrl
|
avatarUrl = publicRoom.avatarUrl,
|
||||||
|
homeServer = roomDirectoryData.homeServer
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ class RoomPreviewNoPreviewFragment @Inject constructor(
|
||||||
|
|
||||||
roomPreviewNoPreviewJoin.callback = object : ButtonStateView.Callback {
|
roomPreviewNoPreviewJoin.callback = object : ButtonStateView.Callback {
|
||||||
override fun onButtonClicked() {
|
override fun onButtonClicked() {
|
||||||
roomPreviewViewModel.handle(RoomPreviewAction.Join(roomPreviewData.roomAlias))
|
roomPreviewViewModel.handle(RoomPreviewAction.Join)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRetryClicked() {
|
override fun onRetryClicked() {
|
||||||
|
|
|
@ -22,7 +22,9 @@ 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.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.query.QueryStringValue
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.room.members.ChangeMembershipState
|
||||||
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.roomSummaryQueryParams
|
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
|
@ -32,7 +34,7 @@ import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.features.roomdirectory.JoinState
|
import im.vector.riotx.features.roomdirectory.JoinState
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState,
|
class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val initialState: RoomPreviewViewState,
|
||||||
private val session: Session)
|
private val session: Session)
|
||||||
: VectorViewModel<RoomPreviewViewState, RoomPreviewAction, EmptyViewEvents>(initialState) {
|
: VectorViewModel<RoomPreviewViewState, RoomPreviewAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@ -52,30 +54,41 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Observe joined room (from the sync)
|
// Observe joined room (from the sync)
|
||||||
observeJoinedRooms()
|
observeRoomSummary()
|
||||||
|
observeMembershipChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeJoinedRooms() {
|
private fun observeRoomSummary() {
|
||||||
val queryParams = roomSummaryQueryParams {
|
val queryParams = roomSummaryQueryParams {
|
||||||
memberships = listOf(Membership.JOIN)
|
roomId = QueryStringValue.Equals(initialState.roomId)
|
||||||
}
|
}
|
||||||
session
|
session
|
||||||
.rx()
|
.rx()
|
||||||
.liveRoomSummaries(queryParams)
|
.liveRoomSummaries(queryParams)
|
||||||
.subscribe { list ->
|
.subscribe { list ->
|
||||||
withState { state ->
|
val isRoomJoined = list.any {
|
||||||
val isRoomJoined = list
|
it.membership == Membership.JOIN
|
||||||
?.map { it.roomId }
|
}
|
||||||
?.toList()
|
|
||||||
?.contains(state.roomId) == true
|
|
||||||
|
|
||||||
if (isRoomJoined) {
|
if (isRoomJoined) {
|
||||||
setState {
|
setState { copy(roomJoinState = JoinState.JOINED) }
|
||||||
copy(
|
|
||||||
roomJoinState = JoinState.JOINED
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.disposeOnClear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeMembershipChanges() {
|
||||||
|
session.rx()
|
||||||
|
.liveRoomChangeMembershipState()
|
||||||
|
.subscribe {
|
||||||
|
val changeMembership = it[initialState.roomId] ?: ChangeMembershipState.Unknown
|
||||||
|
val joinState = when (changeMembership) {
|
||||||
|
is ChangeMembershipState.Joining -> JoinState.JOINING
|
||||||
|
is ChangeMembershipState.FailedJoining -> JoinState.JOINING_ERROR
|
||||||
|
// Other cases are handled by room summary
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (joinState != null) {
|
||||||
|
setState { copy(roomJoinState = joinState) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.disposeOnClear()
|
.disposeOnClear()
|
||||||
|
@ -83,37 +96,27 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
|
||||||
|
|
||||||
override fun handle(action: RoomPreviewAction) {
|
override fun handle(action: RoomPreviewAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomPreviewAction.Join -> handleJoinRoom(action)
|
is RoomPreviewAction.Join -> handleJoinRoom()
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleJoinRoom(action: RoomPreviewAction.Join) = withState { state ->
|
private fun handleJoinRoom() = withState { state ->
|
||||||
if (state.roomJoinState == JoinState.JOINING) {
|
if (state.roomJoinState == JoinState.JOINING) {
|
||||||
// Request already sent, should not happen
|
// Request already sent, should not happen
|
||||||
Timber.w("Try to join an already joining room. Should not happen")
|
Timber.w("Try to join an already joining room. Should not happen")
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
val viaServers = state.homeServer?.let {
|
||||||
setState {
|
listOf(it)
|
||||||
copy(
|
} ?: emptyList()
|
||||||
roomJoinState = JoinState.JOINING,
|
session.joinRoom(state.roomId, viaServers = viaServers, callback = object : MatrixCallback<Unit> {
|
||||||
lastError = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
session.joinRoom(action.roomAlias ?: state.roomId, callback = object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
// 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
|
// Instead, we wait for the room to be joined
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
setState {
|
setState { copy(lastError = failure) }
|
||||||
copy(
|
|
||||||
roomJoinState = JoinState.JOINING_ERROR,
|
|
||||||
lastError = failure
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,21 @@ import im.vector.riotx.features.roomdirectory.JoinState
|
||||||
data class RoomPreviewViewState(
|
data class RoomPreviewViewState(
|
||||||
// The room id
|
// The room id
|
||||||
val roomId: String = "",
|
val roomId: String = "",
|
||||||
|
val roomAlias: String? = null,
|
||||||
|
/**
|
||||||
|
* The server name (might be null)
|
||||||
|
* Set null when the server is the current user's home server.
|
||||||
|
*/
|
||||||
|
val homeServer: String? = null,
|
||||||
// Current state of the room in preview
|
// Current state of the room in preview
|
||||||
val roomJoinState: JoinState = JoinState.NOT_JOINED,
|
val roomJoinState: JoinState = JoinState.NOT_JOINED,
|
||||||
// Last error of join room request
|
// Last error of join room request
|
||||||
val lastError: Throwable? = null
|
val lastError: Throwable? = null
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomPreviewData) : this(roomId = args.roomId)
|
constructor(args: RoomPreviewData) : this(
|
||||||
|
roomId = args.roomId,
|
||||||
|
roomAlias = args.roomAlias,
|
||||||
|
homeServer = args.homeServer
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,6 @@ class RoomProfileFragment @Inject constructor(
|
||||||
when (it) {
|
when (it) {
|
||||||
is RoomProfileViewEvents.Loading -> showLoading(it.message)
|
is RoomProfileViewEvents.Loading -> showLoading(it.message)
|
||||||
is RoomProfileViewEvents.Failure -> showFailure(it.throwable)
|
is RoomProfileViewEvents.Failure -> showFailure(it.throwable)
|
||||||
is RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom()
|
|
||||||
is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink)
|
is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink)
|
||||||
RoomProfileViewEvents.OnChangeAvatarSuccess -> dismissLoadingDialog()
|
RoomProfileViewEvents.OnChangeAvatarSuccess -> dismissLoadingDialog()
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
|
|
|
@ -25,7 +25,6 @@ sealed class RoomProfileViewEvents : VectorViewEvents {
|
||||||
data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
|
data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
|
||||||
data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
|
data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
|
||||||
|
|
||||||
object OnLeaveRoomSuccess : RoomProfileViewEvents()
|
|
||||||
object OnChangeAvatarSuccess : RoomProfileViewEvents()
|
object OnChangeAvatarSuccess : RoomProfileViewEvents()
|
||||||
data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents()
|
data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents()
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted private val ini
|
||||||
_viewEvents.post(RoomProfileViewEvents.Loading(stringProvider.getString(R.string.room_profile_leaving_room)))
|
_viewEvents.post(RoomProfileViewEvents.Loading(stringProvider.getString(R.string.room_profile_leaving_room)))
|
||||||
room.leave(null, object : MatrixCallback<Unit> {
|
room.leave(null, object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
_viewEvents.post(RoomProfileViewEvents.OnLeaveRoomSuccess)
|
// Do nothing, we will be closing the room automatically when it will get back from sync
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
|
|
|
@ -57,34 +57,35 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/inviteIdentifierView" />
|
app:layout_constraintTop_toBottomOf="@id/inviteIdentifierView" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<im.vector.riotx.core.platform.ButtonStateView
|
||||||
android:id="@+id/inviteRejectView"
|
|
||||||
style="@style/VectorButtonStyleText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="4dp"
|
|
||||||
android:layout_marginRight="4dp"
|
|
||||||
android:minWidth="120dp"
|
|
||||||
android:text="@string/reject"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/inviteAcceptView"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/inviteLabelView" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/inviteAcceptView"
|
android:id="@+id/inviteAcceptView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginLeft="4dp"
|
|
||||||
android:minWidth="120dp"
|
android:minWidth="120dp"
|
||||||
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="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
app:layout_constraintStart_toEndOf="@+id/inviteRejectView"
|
app:layout_constraintStart_toEndOf="@+id/inviteRejectView"
|
||||||
app:layout_constraintTop_toTopOf="@id/inviteRejectView" />
|
app:layout_constraintTop_toTopOf="@id/inviteRejectView" />
|
||||||
|
|
||||||
|
<im.vector.riotx.core.platform.ButtonStateView
|
||||||
|
android:id="@+id/inviteRejectView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minWidth="120dp"
|
||||||
|
app:bsv_button_text="@string/reject"
|
||||||
|
app:bsv_loaded_image_src="@drawable/ic_tick"
|
||||||
|
app:bsv_use_flat_button="true"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/inviteAcceptView"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/inviteLabelView"/>
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="16dp"
|
android:layout_height="16dp"
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
android:layout_width="32dp"
|
android:layout_width="32dp"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:indeterminateOnly="true"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
tools:layout_gravity="center_horizontal"
|
tools:layout_gravity="center_horizontal"
|
||||||
tools:layout_marginTop="80dp" />
|
tools:layout_marginTop="80dp" />
|
||||||
|
|
Loading…
Reference in a new issue