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
}
fun isActive(): Boolean {
return activeMemberships().contains(this)
}
companion object {
fun activeMemberships(): List<Membership> {
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> {
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.EventType
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.internal.session.user.UserEntityFactory
import io.realm.Realm
@ -35,7 +34,7 @@ internal class RoomMemberEventHandler @Inject constructor() {
val userId = event.stateKey ?: return false
val roomMemberEntity = RoomMemberEntityFactory.create(roomId, userId, roomMember)
realm.insertOrUpdate(roomMemberEntity)
if (roomMember.membership in Membership.activeMemberships()) {
if (roomMember.membership.isActive()) {
val userEntity = UserEntityFactory.create(userId, roomMember)
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.CurrentStateEventEntity
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.find
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.where
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.mapWithProgress
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 cryptoService: DefaultCryptoService,
private val roomMemberEventHandler: RoomMemberEventHandler,
@UserId private val userId: String,
private val eventBus: EventBus) {
sealed class HandlingStrategy {
@ -208,8 +211,6 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
roomId: String,
roomSync: RoomSync): RoomEntity {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
roomEntity.membership = Membership.LEAVE
roomEntity.chunks.deleteAllFromRealm()
for (event in roomSync.state?.events.orEmpty()) {
if (event.eventId == null || event.stateKey == null) {
continue
@ -221,7 +222,26 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
}
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
}

View File

@ -19,6 +19,7 @@ package im.vector.riotx.features.home.room.detail
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
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.features.home.room.breadcrumbs.BreadcrumbsFragment
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.RequireActiveMembershipViewState
import kotlinx.android.synthetic.main.activity_room_detail.*
@ -91,11 +93,21 @@ class RoomDetailActivity :
}
.disposeOnDestroy()
requireActiveMembershipViewModel.observeViewEvents { finish() }
requireActiveMembershipViewModel.observeViewEvents {
when (it) {
is RequireActiveMembershipViewEvents.RoomLeft -> handleRoomLeft(it)
}
}
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) {
drawerLayout.closeDrawer(GravityCompat.START)
// 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
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.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
import com.squareup.inject.assisted.Assisted
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.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.unwrap
import im.vector.riotx.R
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.disposables.Disposable
import timber.log.Timber
import im.vector.riotx.core.resources.StringProvider
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
/**
* This ViewModel observe a room summary and notify when the room is left
*/
class RequireActiveMembershipViewModel @AssistedInject constructor(
@Assisted initialState: RequireActiveMembershipViewState,
private val stringProvider: StringProvider,
private val session: Session)
: 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 {
observeRoomSummary(initialState.roomId)
observeRoomSummary()
}
private fun observeRoomSummary(roomId: String?) {
currentDisposable?.dispose()
currentDisposable = roomId
?.let { session.getRoom(it) }
?.let { room ->
room.rx().liveRoomSummary()
private fun observeRoomSummary() {
roomIdObservable
.unwrap()
.subscribe {
if (it.membership.isLeft()) {
Timber.w("The room has been left")
_viewEvents.post(RequireActiveMembershipViewEvents.RoomLeft)
}
.switchMap { roomId ->
val room = session.getRoom(roomId) ?: return@switchMap Observable.just(Optional.empty<RequireActiveMembershipViewEvents.RoomLeft>())
room.rx()
.liveRoomSummary()
.unwrap()
.observeOn(Schedulers.computation())
.map { mapToLeftViewEvent(room, it) }
}
.unwrap()
.subscribe { event ->
_viewEvents.post(event)
}
.disposeOnClear()
}
override fun onCleared() {
super.onCleared()
currentDisposable?.dispose()
private fun mapToLeftViewEvent(room: Room, roomSummary: RoomSummary): Optional<RequireActiveMembershipViewEvents.RoomLeft> {
if (roomSummary.membership.isActive()) {
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) {
@ -89,7 +127,7 @@ class RequireActiveMembershipViewModel @AssistedInject constructor(
setState {
copy(roomId = action.roomId)
}
observeRoomSummary(action.roomId)
roomIdObservable.accept(Optional.from(action.roomId))
}
}.exhaustive
}

View File

@ -19,6 +19,7 @@ package im.vector.riotx.features.roommemberprofile
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import com.airbnb.mvrx.MvRx
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.platform.ToolbarConfigurable
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.RequireActiveMembershipViewState
import javax.inject.Inject
@ -66,10 +68,21 @@ class RoomMemberProfileActivity :
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) {
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.Intent
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import com.airbnb.mvrx.MvRx
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.platform.ToolbarConfigurable
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.RequireActiveMembershipViewState
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
@ -86,7 +88,18 @@ class RoomProfileActivity :
}
.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() {