Membership: refact a bit and add a left message when kicked or banned

This commit is contained in:
ganfra 2020-06-12 15:29:07 +02:00
parent 6ca69a9947
commit 171a945de9
9 changed files with 134 additions and 31 deletions

View File

@ -44,6 +44,10 @@ enum class Membership(val value: String) {
return this == KNOCK || this == LEAVE || this == BAN return this == KNOCK || this == LEAVE || this == BAN
} }
fun isActive(): Boolean {
return activeMemberships().contains(this)
}
companion object { companion object {
fun activeMemberships(): List<Membership> { fun activeMemberships(): List<Membership> {
return listOf(INVITE, JOIN) return listOf(INVITE, JOIN)

View File

@ -47,6 +47,10 @@ data class Optional<T : Any> constructor(private val value: T?) {
fun <T : Any> from(value: T?): Optional<T> { fun <T : Any> from(value: T?): Optional<T> {
return Optional(value) return Optional(value)
} }
fun <T: Any> empty(): Optional<T> {
return Optional(null)
}
} }
} }

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.session.room.membership
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.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.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMemberContent import im.vector.matrix.android.api.session.room.model.RoomMemberContent
import im.vector.matrix.android.internal.session.user.UserEntityFactory import im.vector.matrix.android.internal.session.user.UserEntityFactory
import io.realm.Realm import io.realm.Realm
@ -35,7 +34,7 @@ internal class RoomMemberEventHandler @Inject constructor() {
val userId = event.stateKey ?: return false val userId = event.stateKey ?: return false
val roomMemberEntity = RoomMemberEntityFactory.create(roomId, userId, roomMember) val roomMemberEntity = RoomMemberEntityFactory.create(roomId, userId, roomMember)
realm.insertOrUpdate(roomMemberEntity) realm.insertOrUpdate(roomMemberEntity)
if (roomMember.membership in Membership.activeMemberships()) { if (roomMember.membership.isActive()) {
val userEntity = UserEntityFactory.create(userId, roomMember) val userEntity = UserEntityFactory.create(userId, roomMember)
realm.insertOrUpdate(userEntity) realm.insertOrUpdate(userEntity)
} }

View File

@ -35,6 +35,7 @@ import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity
import im.vector.matrix.android.internal.database.query.copyToRealmOrIgnore import im.vector.matrix.android.internal.database.query.copyToRealmOrIgnore
import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.findLastForwardChunkOfRoom import im.vector.matrix.android.internal.database.query.findLastForwardChunkOfRoom
@ -42,6 +43,7 @@ import im.vector.matrix.android.internal.database.query.getOrCreate
import im.vector.matrix.android.internal.database.query.getOrNull import im.vector.matrix.android.internal.database.query.getOrNull
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.di.MoshiProvider
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.RoomSummaryUpdater import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
@ -67,6 +69,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
private val roomFullyReadHandler: RoomFullyReadHandler, private val roomFullyReadHandler: RoomFullyReadHandler,
private val cryptoService: DefaultCryptoService, private val cryptoService: DefaultCryptoService,
private val roomMemberEventHandler: RoomMemberEventHandler, private val roomMemberEventHandler: RoomMemberEventHandler,
@UserId private val userId: String,
private val eventBus: EventBus) { private val eventBus: EventBus) {
sealed class HandlingStrategy { sealed class HandlingStrategy {
@ -208,8 +211,6 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
roomId: String, roomId: String,
roomSync: RoomSync): RoomEntity { roomSync: RoomSync): RoomEntity {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId) val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
roomEntity.membership = Membership.LEAVE
roomEntity.chunks.deleteAllFromRealm()
for (event in roomSync.state?.events.orEmpty()) { for (event in roomSync.state?.events.orEmpty()) {
if (event.eventId == null || event.stateKey == null) { if (event.eventId == null || event.stateKey == null) {
continue continue
@ -221,7 +222,26 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
} }
roomMemberEventHandler.handle(realm, roomId, event) roomMemberEventHandler.handle(realm, roomId, event)
} }
roomSummaryUpdater.update(realm, roomId, Membership.LEAVE, roomSync.summary, roomSync.unreadNotifications) for (event in roomSync.timeline?.events.orEmpty()) {
if (event.eventId == null || event.senderId == null) {
continue
}
val eventEntity = event.toEntity(roomId, SendState.SYNCED).copyToRealmOrIgnore(realm)
if (event.stateKey != null) {
CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply {
eventId = event.eventId
root = eventEntity
}
if (event.type == EventType.STATE_ROOM_MEMBER) {
roomMemberEventHandler.handle(realm, roomEntity.roomId, event)
}
}
}
val leftMember = RoomMemberSummaryEntity.where(realm, roomId, userId).findFirst()
val membership = leftMember?.membership ?: Membership.LEAVE
roomEntity.membership = membership
roomEntity.chunks.deleteAllFromRealm()
roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications)
return roomEntity return roomEntity
} }

View File

@ -19,6 +19,7 @@ package im.vector.riotx.features.home.room.detail
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
@ -31,6 +32,7 @@ import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.riotx.features.home.room.breadcrumbs.BreadcrumbsFragment
import im.vector.riotx.features.room.RequireActiveMembershipAction import im.vector.riotx.features.room.RequireActiveMembershipAction
import im.vector.riotx.features.room.RequireActiveMembershipViewEvents
import im.vector.riotx.features.room.RequireActiveMembershipViewModel import im.vector.riotx.features.room.RequireActiveMembershipViewModel
import im.vector.riotx.features.room.RequireActiveMembershipViewState import im.vector.riotx.features.room.RequireActiveMembershipViewState
import kotlinx.android.synthetic.main.activity_room_detail.* import kotlinx.android.synthetic.main.activity_room_detail.*
@ -91,11 +93,21 @@ class RoomDetailActivity :
} }
.disposeOnDestroy() .disposeOnDestroy()
requireActiveMembershipViewModel.observeViewEvents { finish() } requireActiveMembershipViewModel.observeViewEvents {
when (it) {
is RequireActiveMembershipViewEvents.RoomLeft -> handleRoomLeft(it)
}
}
drawerLayout.addDrawerListener(drawerListener) drawerLayout.addDrawerListener(drawerListener)
} }
private fun handleRoomLeft(roomLeft: RequireActiveMembershipViewEvents.RoomLeft) {
if (roomLeft.leftMessage != null) {
Toast.makeText(this, roomLeft.leftMessage, Toast.LENGTH_LONG).show()
}
finish()
}
private fun switchToRoom(switchToRoom: RoomDetailSharedAction.SwitchToRoom) { private fun switchToRoom(switchToRoom: RoomDetailSharedAction.SwitchToRoom) {
drawerLayout.closeDrawer(GravityCompat.START) drawerLayout.closeDrawer(GravityCompat.START)
// Do not replace the Fragment if it's the same roomId // Do not replace the Fragment if it's the same roomId

View File

@ -19,5 +19,5 @@ package im.vector.riotx.features.room
import im.vector.riotx.core.platform.VectorViewEvents import im.vector.riotx.core.platform.VectorViewEvents
sealed class RequireActiveMembershipViewEvents : VectorViewEvents { sealed class RequireActiveMembershipViewEvents : VectorViewEvents {
object RoomLeft : RequireActiveMembershipViewEvents() data class RoomLeft(val leftMessage: String?) : RequireActiveMembershipViewEvents()
} }

View File

@ -20,21 +20,31 @@ import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
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.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.events.model.EventType
import im.vector.matrix.android.api.session.room.Room
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.util.Optional
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.R
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 io.reactivex.disposables.Disposable import im.vector.riotx.core.resources.StringProvider
import timber.log.Timber import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
/** /**
* This ViewModel observe a room summary and notify when the room is left * This ViewModel observe a room summary and notify when the room is left
*/ */
class RequireActiveMembershipViewModel @AssistedInject constructor( class RequireActiveMembershipViewModel @AssistedInject constructor(
@Assisted initialState: RequireActiveMembershipViewState, @Assisted initialState: RequireActiveMembershipViewState,
private val stringProvider: StringProvider,
private val session: Session) private val session: Session)
: VectorViewModel<RequireActiveMembershipViewState, RequireActiveMembershipAction, RequireActiveMembershipViewEvents>(initialState) { : VectorViewModel<RequireActiveMembershipViewState, RequireActiveMembershipAction, RequireActiveMembershipViewEvents>(initialState) {
@ -55,32 +65,60 @@ class RequireActiveMembershipViewModel @AssistedInject constructor(
} }
} }
private var currentDisposable: Disposable? = null private val roomIdObservable = BehaviorRelay.createDefault(Optional.from(initialState.roomId))
init { init {
observeRoomSummary(initialState.roomId) observeRoomSummary()
} }
private fun observeRoomSummary(roomId: String?) { private fun observeRoomSummary() {
currentDisposable?.dispose() roomIdObservable
currentDisposable = roomId
?.let { session.getRoom(it) }
?.let { room ->
room.rx().liveRoomSummary()
.unwrap() .unwrap()
.subscribe { .switchMap { roomId ->
if (it.membership.isLeft()) { val room = session.getRoom(roomId) ?: return@switchMap Observable.just(Optional.empty<RequireActiveMembershipViewEvents.RoomLeft>())
Timber.w("The room has been left") room.rx()
_viewEvents.post(RequireActiveMembershipViewEvents.RoomLeft) .liveRoomSummary()
} .unwrap()
.observeOn(Schedulers.computation())
.map { mapToLeftViewEvent(room, it) }
} }
.unwrap()
.subscribe { event ->
_viewEvents.post(event)
} }
.disposeOnClear()
} }
override fun onCleared() { private fun mapToLeftViewEvent(room: Room, roomSummary: RoomSummary): Optional<RequireActiveMembershipViewEvents.RoomLeft> {
super.onCleared() if (roomSummary.membership.isActive()) {
currentDisposable?.dispose() return Optional.empty()
}
val senderId = room.getStateEvent(EventType.STATE_ROOM_MEMBER, QueryStringValue.Equals(session.myUserId))?.senderId
val senderDisplayName = senderId?.takeIf { it != session.myUserId }?.let {
room.getRoomMember(it)?.displayName ?: it
}
val viewEvent = when (roomSummary.membership) {
Membership.LEAVE -> {
val message = senderDisplayName?.let {
stringProvider.getString(R.string.has_been_kicked, roomSummary.displayName, it)
}
RequireActiveMembershipViewEvents.RoomLeft(message)
}
Membership.KNOCK -> {
val message = senderDisplayName?.let {
stringProvider.getString(R.string.has_been_kicked, roomSummary.displayName, it)
}
RequireActiveMembershipViewEvents.RoomLeft(message)
}
Membership.BAN -> {
val message = senderDisplayName?.let {
stringProvider.getString(R.string.has_been_banned, roomSummary.displayName, it)
}
RequireActiveMembershipViewEvents.RoomLeft(message)
}
else -> null
}
return Optional.from(viewEvent)
} }
override fun handle(action: RequireActiveMembershipAction) { override fun handle(action: RequireActiveMembershipAction) {
@ -89,7 +127,7 @@ class RequireActiveMembershipViewModel @AssistedInject constructor(
setState { setState {
copy(roomId = action.roomId) copy(roomId = action.roomId)
} }
observeRoomSummary(action.roomId) roomIdObservable.accept(Optional.from(action.roomId))
} }
}.exhaustive }.exhaustive
} }

View File

@ -19,6 +19,7 @@ package im.vector.riotx.features.roommemberprofile
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.widget.Toast
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
@ -27,6 +28,7 @@ import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.addFragment import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.room.RequireActiveMembershipViewEvents
import im.vector.riotx.features.room.RequireActiveMembershipViewModel import im.vector.riotx.features.room.RequireActiveMembershipViewModel
import im.vector.riotx.features.room.RequireActiveMembershipViewState import im.vector.riotx.features.room.RequireActiveMembershipViewState
import javax.inject.Inject import javax.inject.Inject
@ -66,10 +68,21 @@ class RoomMemberProfileActivity :
addFragment(R.id.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs) addFragment(R.id.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs)
} }
requireActiveMembershipViewModel.observeViewEvents { finish() } requireActiveMembershipViewModel.observeViewEvents {
when (it) {
is RequireActiveMembershipViewEvents.RoomLeft -> handleRoomLeft(it)
}
}
} }
override fun configure(toolbar: Toolbar) { override fun configure(toolbar: Toolbar) {
configureToolbar(toolbar) configureToolbar(toolbar)
} }
private fun handleRoomLeft(roomLeft: RequireActiveMembershipViewEvents.RoomLeft) {
if (roomLeft.leftMessage != null) {
Toast.makeText(this, roomLeft.leftMessage, Toast.LENGTH_LONG).show()
}
finish()
}
} }

View File

@ -19,6 +19,7 @@ package im.vector.riotx.features.roomprofile
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.widget.Toast
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
@ -28,6 +29,7 @@ import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.extensions.addFragmentToBackstack import im.vector.riotx.core.extensions.addFragmentToBackstack
import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.room.RequireActiveMembershipViewEvents
import im.vector.riotx.features.room.RequireActiveMembershipViewModel import im.vector.riotx.features.room.RequireActiveMembershipViewModel
import im.vector.riotx.features.room.RequireActiveMembershipViewState import im.vector.riotx.features.room.RequireActiveMembershipViewState
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
@ -86,7 +88,18 @@ class RoomProfileActivity :
} }
.disposeOnDestroy() .disposeOnDestroy()
requireActiveMembershipViewModel.observeViewEvents { finish() } requireActiveMembershipViewModel.observeViewEvents {
when (it) {
is RequireActiveMembershipViewEvents.RoomLeft -> handleRoomLeft(it)
}
}
}
private fun handleRoomLeft(roomLeft: RequireActiveMembershipViewEvents.RoomLeft) {
if (roomLeft.leftMessage != null) {
Toast.makeText(this, roomLeft.leftMessage, Toast.LENGTH_LONG).show()
}
finish()
} }
private fun openRoomUploads() { private fun openRoomUploads() {