AutoAcceptInvite: refact and hide behind flag

This commit is contained in:
ganfra 2021-06-18 17:24:51 +02:00
parent 646f00f3fc
commit 6b10406622
19 changed files with 325 additions and 82 deletions

View File

@ -125,6 +125,12 @@ interface RoomService {
*/ */
suspend fun deleteRoomAlias(roomAlias: String) suspend fun deleteRoomAlias(roomAlias: String)
/**
* Return the current local changes membership for the given room.
* see [getChangeMembershipsLive] for more details.
*/
fun getChangeMemberships(roomIdOrAlias: String): ChangeMembershipState
/** /**
* Return a live data of all local changes membership that happened since the session has been opened. * Return a live data of all local changes membership that 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 allows you to track this in your client to known what is currently being processed by the SDK.

View File

@ -386,4 +386,7 @@ internal abstract class SessionModule {
@Binds @Binds
abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor
} }

View File

@ -134,6 +134,10 @@ internal class DefaultRoomService @Inject constructor(
deleteRoomAliasTask.execute(DeleteRoomAliasTask.Params(roomAlias)) deleteRoomAliasTask.execute(DeleteRoomAliasTask.Params(roomAlias))
} }
override fun getChangeMemberships(roomIdOrAlias: String): ChangeMembershipState {
return roomChangeMembershipStateDataSource.getState(roomIdOrAlias)
}
override fun getChangeMembershipsLive(): LiveData<Map<String, ChangeMembershipState>> { override fun getChangeMembershipsLive(): LiveData<Map<String, ChangeMembershipState>> {
return roomChangeMembershipStateDataSource.getLiveStates() return roomChangeMembershipStateDataSource.getLiveStates()
} }

View File

@ -21,6 +21,7 @@ import androidx.lifecycle.MutableLiveData
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -30,7 +31,7 @@ import javax.inject.Inject
internal class RoomChangeMembershipStateDataSource @Inject constructor() { internal class RoomChangeMembershipStateDataSource @Inject constructor() {
private val mutableLiveStates = MutableLiveData<Map<String, ChangeMembershipState>>(emptyMap()) private val mutableLiveStates = MutableLiveData<Map<String, ChangeMembershipState>>(emptyMap())
private val states = HashMap<String, ChangeMembershipState>() private val states = ConcurrentHashMap<String, ChangeMembershipState>()
/** /**
* This will update local states to be synced with the server. * This will update local states to be synced with the server.

View File

@ -54,6 +54,10 @@ internal class DefaultJoinRoomTask @Inject constructor(
) : JoinRoomTask { ) : JoinRoomTask {
override suspend fun execute(params: JoinRoomTask.Params) { override suspend fun execute(params: JoinRoomTask.Params) {
val currentState = roomChangeMembershipStateDataSource.getState(params.roomIdOrAlias)
if (currentState.isInProgress() || currentState == ChangeMembershipState.Joined) {
return
}
roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.Joining) roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.Joining)
val joinRoomResponse = try { val joinRoomResponse = try {
executeRequest(globalErrorReceiver) { executeRequest(globalErrorReceiver) {

View File

@ -22,22 +22,16 @@ import androidx.lifecycle.OnLifecycleEvent
import arrow.core.Option import arrow.core.Option
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.utils.BehaviorDataSource import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.invite.InvitesAcceptor
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.group.model.GroupSummary
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -58,7 +52,8 @@ fun RoomGroupingMethod.group() = (this as? RoomGroupingMethod.ByLegacyGroup)?.gr
class AppStateHandler @Inject constructor( class AppStateHandler @Inject constructor(
private val sessionDataSource: ActiveSessionDataSource, private val sessionDataSource: ActiveSessionDataSource,
private val uiStateRepository: UiStateRepository, private val uiStateRepository: UiStateRepository,
private val activeSessionHolder: ActiveSessionHolder private val activeSessionHolder: ActiveSessionHolder,
private val invitesAcceptor: InvitesAcceptor
) : LifecycleObserver { ) : LifecycleObserver {
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
@ -102,13 +97,13 @@ class AppStateHandler @Inject constructor(
} }
} }
private fun observeActiveSession(){ private fun observeActiveSession() {
sessionDataSource.observe() sessionDataSource.observe()
.distinctUntilChanged() .distinctUntilChanged()
.subscribe { .subscribe {
// sessionDataSource could already return a session while activeSession holder still returns null // sessionDataSource could already return a session while activeSession holder still returns null
it.orNull()?.let { session -> it.orNull()?.let { session ->
observeInvitesForAutoAccept(session) invitesAcceptor.onSessionActive(session)
if (uiStateRepository.isGroupingMethodSpace(session.sessionId)) { if (uiStateRepository.isGroupingMethodSpace(session.sessionId)) {
setCurrentSpace(uiStateRepository.getSelectedSpace(session.sessionId), session) setCurrentSpace(uiStateRepository.getSelectedSpace(session.sessionId), session)
} else { } else {
@ -120,41 +115,6 @@ class AppStateHandler @Inject constructor(
} }
} }
private fun observeInvitesForAutoAccept(session: Session?) {
if (session == null) return
val roomQueryParams = roomSummaryQueryParams {
this.memberships = listOf(Membership.INVITE)
}
val rxSession = session.rx()
Observable
.combineLatest(
rxSession.liveRoomSummaries(roomQueryParams).debounce(1, TimeUnit.SECONDS),
rxSession.liveRoomChangeMembershipState().debounce(1, TimeUnit.SECONDS),
{ invitedRooms, membershipsChanged ->
val roomIdsToJoin = mutableListOf<String>()
for (room in invitedRooms) {
val roomMembershipChanged = membershipsChanged[room.roomId] ?: ChangeMembershipState.Unknown
if (roomMembershipChanged != ChangeMembershipState.Joined && !roomMembershipChanged.isInProgress()) {
roomIdsToJoin.add(room.roomId)
}
}
roomIdsToJoin
}
)
.doOnNext { roomIdsToJoin ->
session.coroutineScope.launch {
for (roomId in roomIdsToJoin) {
Timber.v("Auto accept invite for room: $roomId")
tryOrNull { session.joinRoom(roomId) }
}
}
}
.subscribe()
.also {
compositeDisposable.add(it)
}
}
fun safeActiveSpaceId(): String? { fun safeActiveSpaceId(): String? {
return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId
} }

View File

@ -50,6 +50,7 @@ import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
import im.vector.app.features.home.room.list.RoomListModule import im.vector.app.features.home.room.list.RoomListModule
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.InviteUsersToRoomActivity import im.vector.app.features.invite.InviteUsersToRoomActivity
import im.vector.app.features.invite.VectorInviteView import im.vector.app.features.invite.VectorInviteView
import im.vector.app.features.link.LinkHandlerActivity import im.vector.app.features.link.LinkHandlerActivity
@ -122,6 +123,7 @@ interface ScreenComponent {
fun errorFormatter(): ErrorFormatter fun errorFormatter(): ErrorFormatter
fun uiStateRepository(): UiStateRepository fun uiStateRepository(): UiStateRepository
fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog
fun autoAcceptInvites(): AutoAcceptInvites
/* ========================================================================================== /* ==========================================================================================
* Activities * Activities

View File

@ -42,6 +42,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorPr
import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder
import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.VectorHtmlCompressor import im.vector.app.features.html.VectorHtmlCompressor
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import im.vector.app.features.notifications.NotifiableEventResolver import im.vector.app.features.notifications.NotifiableEventResolver
@ -160,6 +161,8 @@ interface VectorComponent {
fun pinLocker(): PinLocker fun pinLocker(): PinLocker
fun autoAcceptInvites(): AutoAcceptInvites
fun webRtcCallManager(): WebRtcCallManager fun webRtcCallManager(): WebRtcCallManager
fun roomSummaryHolder(): RoomSummariesHolder fun roomSummaryHolder(): RoomSummariesHolder

View File

@ -25,6 +25,8 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import im.vector.app.core.error.DefaultErrorFormatter import im.vector.app.core.error.DefaultErrorFormatter
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.CompileTimeAutoAcceptInvites
import im.vector.app.features.navigation.DefaultNavigator import im.vector.app.features.navigation.DefaultNavigator
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinCodeStore
@ -105,4 +107,7 @@ abstract class VectorModule {
@Binds @Binds
abstract fun bindPinCodeStore(store: SharedPrefPinCodeStore): PinCodeStore abstract fun bindPinCodeStore(store: SharedPrefPinCodeStore): PinCodeStore
@Binds
abstract fun bindAutoAcceptInvites(autoAcceptInvites: CompileTimeAutoAcceptInvites): AutoAcceptInvites
} }

View File

@ -31,6 +31,7 @@ import im.vector.app.features.call.dialpad.DialPadLookup
import im.vector.app.features.call.lookup.CallProtocolsChecker import im.vector.app.features.call.lookup.CallProtocolsChecker
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.createdirect.DirectRoomHelper
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -56,7 +57,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
private val uiStateRepository: UiStateRepository, private val uiStateRepository: UiStateRepository,
private val callManager: WebRtcCallManager, private val callManager: WebRtcCallManager,
private val directRoomHelper: DirectRoomHelper, private val directRoomHelper: DirectRoomHelper,
private val appStateHandler: AppStateHandler) private val appStateHandler: AppStateHandler,
private val autoAcceptInvites: AutoAcceptInvites)
: VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState), : VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState),
CallProtocolsChecker.Listener { CallProtocolsChecker.Listener {
@ -204,8 +206,25 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
} }
is RoomGroupingMethod.BySpace -> { is RoomGroupingMethod.BySpace -> {
val activeSpaceRoomId = groupingMethod.spaceSummary?.roomId val activeSpaceRoomId = groupingMethod.spaceSummary?.roomId
val dmInvites = 0 var dmInvites = 0
val roomsInvite = 0 var roomsInvite = 0
if(!autoAcceptInvites.hideInvites) {
dmInvites = session.getRoomSummaries(
roomSummaryQueryParams {
memberships = listOf(Membership.INVITE)
roomCategoryFilter = RoomCategoryFilter.ONLY_DM
activeSpaceFilter = activeSpaceRoomId?.let { ActiveSpaceFilter.ActiveSpace(it) } ?: ActiveSpaceFilter.None
}
).size
roomsInvite = session.getRoomSummaries(
roomSummaryQueryParams {
memberships = listOf(Membership.INVITE)
roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(groupingMethod.spaceSummary?.roomId)
}
).size
}
val dmRooms = session.getNotificationCountForRooms( val dmRooms = session.getNotificationCountForRooms(
roomSummaryQueryParams { roomSummaryQueryParams {

View File

@ -29,6 +29,7 @@ import im.vector.app.RoomGroupingMethod
import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@ -54,7 +55,8 @@ data class CountInfo(
class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initialState: UnreadMessagesState, class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initialState: UnreadMessagesState,
session: Session, session: Session,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
appStateHandler: AppStateHandler) appStateHandler: AppStateHandler,
private val autoAcceptInvites: AutoAcceptInvites)
: VectorViewModel<UnreadMessagesState, EmptyAction, EmptyViewEvents>(initialState) { : VectorViewModel<UnreadMessagesState, EmptyAction, EmptyViewEvents>(initialState) {
@AssistedFactory @AssistedFactory
@ -92,7 +94,17 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia
this.activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null) this.activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null)
} }
) )
val invites = 0 val invites = if (autoAcceptInvites.hideInvites) {
0
} else {
session.getRoomSummaries(
roomSummaryQueryParams {
this.memberships = listOf(Membership.INVITE)
this.activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null)
}
).size
}
copy( copy(
homeSpaceUnread = RoomAggregateNotificationCount( homeSpaceUnread = RoomAggregateNotificationCount(
counts.notificationCount + invites, counts.notificationCount + invites,
@ -124,8 +136,13 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia
is RoomGroupingMethod.BySpace -> { is RoomGroupingMethod.BySpace -> {
val selectedSpace = appStateHandler.safeActiveSpaceId() val selectedSpace = appStateHandler.safeActiveSpaceId()
val inviteCount = 0 val inviteCount = if (autoAcceptInvites.hideInvites) {
0
} else {
session.getRoomSummaries(
roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) }
).size
}
val totalCount = session.getNotificationCountForRooms( val totalCount = session.getNotificationCountForRooms(
roomSummaryQueryParams { roomSummaryQueryParams {
this.memberships = listOf(Membership.JOIN) this.memberships = listOf(Membership.JOIN)

View File

@ -22,6 +22,7 @@ import im.vector.app.R
import im.vector.app.RoomGroupingMethod import im.vector.app.RoomGroupingMethod
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.invite.AutoAcceptInvites
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -38,6 +39,7 @@ class GroupRoomListSectionBuilder(
val stringProvider: StringProvider, val stringProvider: StringProvider,
val viewModelScope: CoroutineScope, val viewModelScope: CoroutineScope,
val appStateHandler: AppStateHandler, val appStateHandler: AppStateHandler,
private val autoAcceptInvites: AutoAcceptInvites,
val onDisposable: (Disposable) -> Unit, val onDisposable: (Disposable) -> Unit,
val onUdpatable: (UpdatableLivePageResult) -> Unit val onUdpatable: (UpdatableLivePageResult) -> Unit
) : RoomListSectionBuilder { ) : RoomListSectionBuilder {
@ -48,15 +50,15 @@ class GroupRoomListSectionBuilder(
val actualGroupId = appStateHandler.safeActiveGroupId() val actualGroupId = appStateHandler.safeActiveGroupId()
when (mode) { when (mode) {
RoomListDisplayMode.PEOPLE -> { RoomListDisplayMode.PEOPLE -> {
// 3 sections Invites / Fav / Dms // 3 sections Invites / Fav / Dms
buildPeopleSections(sections, activeGroupAwareQueries, actualGroupId) buildPeopleSections(sections, activeGroupAwareQueries, actualGroupId)
} }
RoomListDisplayMode.ROOMS -> { RoomListDisplayMode.ROOMS -> {
// 5 sections invites / Fav / Rooms / Low Priority / Server notice // 5 sections invites / Fav / Rooms / Low Priority / Server notice
buildRoomsSections(sections, activeGroupAwareQueries, actualGroupId) buildRoomsSections(sections, activeGroupAwareQueries, actualGroupId)
} }
RoomListDisplayMode.FILTERED -> { RoomListDisplayMode.FILTERED -> {
// Used when searching for rooms // Used when searching for rooms
withQueryParams( withQueryParams(
{ {
@ -73,17 +75,18 @@ class GroupRoomListSectionBuilder(
) )
} }
RoomListDisplayMode.NOTIFICATIONS -> { RoomListDisplayMode.NOTIFICATIONS -> {
addSection( if (!autoAcceptInvites.hideInvites) {
sections, addSection(
activeGroupAwareQueries, sections,
R.string.invitations_header, activeGroupAwareQueries,
true R.string.invitations_header,
) { true
it.memberships = listOf(Membership.INVITE) ) {
it.roomCategoryFilter = RoomCategoryFilter.ALL it.memberships = listOf(Membership.INVITE)
it.activeGroupId = actualGroupId it.roomCategoryFilter = RoomCategoryFilter.ALL
it.activeGroupId = actualGroupId
}
} }
addSection( addSection(
sections, sections,
activeGroupAwareQueries, activeGroupAwareQueries,
@ -115,6 +118,18 @@ class GroupRoomListSectionBuilder(
private fun buildRoomsSections(sections: MutableList<RoomsSection>, private fun buildRoomsSections(sections: MutableList<RoomsSection>,
activeSpaceAwareQueries: MutableList<UpdatableLivePageResult>, activeSpaceAwareQueries: MutableList<UpdatableLivePageResult>,
actualGroupId: String?) { actualGroupId: String?) {
if (!autoAcceptInvites.hideInvites) {
addSection(
sections,
activeSpaceAwareQueries,
R.string.invitations_header,
true
) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
it.activeGroupId = actualGroupId
}
}
addSection( addSection(
sections, sections,
@ -170,6 +185,17 @@ class GroupRoomListSectionBuilder(
activeSpaceAwareQueries: MutableList<UpdatableLivePageResult>, activeSpaceAwareQueries: MutableList<UpdatableLivePageResult>,
actualGroupId: String? actualGroupId: String?
) { ) {
if (!autoAcceptInvites.hideInvites) {
addSection(sections,
activeSpaceAwareQueries,
R.string.invitations_header,
true
) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
it.activeGroupId = actualGroupId
}
}
addSection( addSection(
sections, sections,

View File

@ -30,6 +30,7 @@ import im.vector.app.RoomGroupingMethod
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -49,7 +50,8 @@ class RoomListViewModel @Inject constructor(
private val session: Session, private val session: Session,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val appStateHandler: AppStateHandler, private val appStateHandler: AppStateHandler,
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences,
private val autoAcceptInvites: AutoAcceptInvites
) : VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) { ) : VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
interface Factory { interface Factory {
@ -126,6 +128,7 @@ class RoomListViewModel @Inject constructor(
appStateHandler, appStateHandler,
viewModelScope, viewModelScope,
suggestedRoomJoiningState, suggestedRoomJoiningState,
autoAcceptInvites,
{ {
it.disposeOnClear() it.disposeOnClear()
}, },
@ -140,6 +143,7 @@ class RoomListViewModel @Inject constructor(
stringProvider, stringProvider,
viewModelScope, viewModelScope,
appStateHandler, appStateHandler,
autoAcceptInvites,
{ {
it.disposeOnClear() it.disposeOnClear()
}, },

View File

@ -18,6 +18,7 @@ package im.vector.app.features.home.room.list
import im.vector.app.AppStateHandler import im.vector.app.AppStateHandler
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject import javax.inject.Inject
@ -26,16 +27,18 @@ import javax.inject.Provider
class RoomListViewModelFactory @Inject constructor(private val session: Provider<Session>, class RoomListViewModelFactory @Inject constructor(private val session: Provider<Session>,
private val appStateHandler: AppStateHandler, private val appStateHandler: AppStateHandler,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val vectorPreferences: VectorPreferences) private val vectorPreferences: VectorPreferences,
private val autoAcceptInvites: AutoAcceptInvites)
: RoomListViewModel.Factory { : RoomListViewModel.Factory {
override fun create(initialState: RoomListViewState): RoomListViewModel { override fun create(initialState: RoomListViewState): RoomListViewModel {
return RoomListViewModel( return RoomListViewModel(
initialState, initialState = initialState,
session.get(), session = session.get(),
stringProvider, stringProvider = stringProvider,
appStateHandler, appStateHandler = appStateHandler,
vectorPreferences vectorPreferences = vectorPreferences,
autoAcceptInvites = autoAcceptInvites
) )
} }
} }

View File

@ -26,6 +26,7 @@ import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.space import im.vector.app.space
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -50,6 +51,7 @@ class SpaceRoomListSectionBuilder(
val appStateHandler: AppStateHandler, val appStateHandler: AppStateHandler,
val viewModelScope: CoroutineScope, val viewModelScope: CoroutineScope,
private val suggestedRoomJoiningState: LiveData<Map<String, Async<Unit>>>, private val suggestedRoomJoiningState: LiveData<Map<String, Async<Unit>>>,
private val autoAcceptInvites: AutoAcceptInvites,
val onDisposable: (Disposable) -> Unit, val onDisposable: (Disposable) -> Unit,
val onUdpatable: (UpdatableLivePageResult) -> Unit, val onUdpatable: (UpdatableLivePageResult) -> Unit,
val onlyOrphansInHome: Boolean = false val onlyOrphansInHome: Boolean = false
@ -66,13 +68,13 @@ class SpaceRoomListSectionBuilder(
val sections = mutableListOf<RoomsSection>() val sections = mutableListOf<RoomsSection>()
val activeSpaceAwareQueries = mutableListOf<RoomListViewModel.ActiveSpaceQueryUpdater>() val activeSpaceAwareQueries = mutableListOf<RoomListViewModel.ActiveSpaceQueryUpdater>()
when (mode) { when (mode) {
RoomListDisplayMode.PEOPLE -> { RoomListDisplayMode.PEOPLE -> {
buildDmSections(sections, activeSpaceAwareQueries) buildDmSections(sections, activeSpaceAwareQueries)
} }
RoomListDisplayMode.ROOMS -> { RoomListDisplayMode.ROOMS -> {
buildRoomsSections(sections, activeSpaceAwareQueries) buildRoomsSections(sections, activeSpaceAwareQueries)
} }
RoomListDisplayMode.FILTERED -> { RoomListDisplayMode.FILTERED -> {
withQueryParams( withQueryParams(
{ {
it.memberships = Membership.activeMemberships() it.memberships = Membership.activeMemberships()
@ -88,6 +90,23 @@ class SpaceRoomListSectionBuilder(
) )
} }
RoomListDisplayMode.NOTIFICATIONS -> { RoomListDisplayMode.NOTIFICATIONS -> {
if (!autoAcceptInvites.hideInvites) {
addSection(
sections = sections,
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.invitations_header,
notifyOfLocalEcho = true,
spaceFilterStrategy = if (onlyOrphansInHome) {
RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL
} else {
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL
},
countRoomAsNotif = true
) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ALL
}
}
addSection( addSection(
sections = sections, sections = sections,
@ -121,6 +140,20 @@ class SpaceRoomListSectionBuilder(
} }
private fun buildRoomsSections(sections: MutableList<RoomsSection>, activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>) { private fun buildRoomsSections(sections: MutableList<RoomsSection>, activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>) {
if (!autoAcceptInvites.hideInvites) {
addSection(
sections = sections,
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.invitations_header,
notifyOfLocalEcho = true,
spaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
countRoomAsNotif = true
) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
}
}
addSection( addSection(
sections, sections,
activeSpaceAwareQueries, activeSpaceAwareQueries,
@ -226,6 +259,18 @@ class SpaceRoomListSectionBuilder(
} }
private fun buildDmSections(sections: MutableList<RoomsSection>, activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>) { private fun buildDmSections(sections: MutableList<RoomsSection>, activeSpaceAwareQueries: MutableList<RoomListViewModel.ActiveSpaceQueryUpdater>) {
if (!autoAcceptInvites.hideInvites) {
addSection(sections = sections,
activeSpaceUpdaters = activeSpaceAwareQueries,
nameRes = R.string.invitations_header,
notifyOfLocalEcho = true,
spaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL,
countRoomAsNotif = true
) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
}
}
addSection(sections, addSection(sections,
activeSpaceAwareQueries, activeSpaceAwareQueries,
@ -350,7 +395,7 @@ class SpaceRoomListSectionBuilder(
activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(currentSpace) activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(currentSpace)
) )
} }
RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL -> { RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL -> {
if (currentSpace == null) { if (currentSpace == null) {
copy( copy(
activeSpaceFilter = ActiveSpaceFilter.None activeSpaceFilter = ActiveSpaceFilter.None
@ -361,7 +406,7 @@ class SpaceRoomListSectionBuilder(
) )
} }
} }
RoomListViewModel.SpaceFilterStrategy.NONE -> this RoomListViewModel.SpaceFilterStrategy.NONE -> this
} }
} }
} }

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2021 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.app.features.invite
import javax.inject.Inject
interface AutoAcceptInvites {
val isEnabled: Boolean
val hideInvites: Boolean
}
class CompileTimeAutoAcceptInvites @Inject constructor() : AutoAcceptInvites {
override val isEnabled = true
override val hideInvites = false
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2021 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.app.features.invite
import im.vector.app.features.session.coroutineScope
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
/**
* This class is responsible for auto accepting invites.
* It's listening to invites and membershipChanges so it can retry automatically if needed.
* This mechanism will be on only if AutoAcceptInvites.isEnabled is true.
*/
@Singleton
class InvitesAcceptor @Inject constructor(private val autoAcceptInvites: AutoAcceptInvites) : Session.Listener {
private val disposables = HashMap<String, Disposable>()
private val semaphore = Semaphore(1)
fun onSessionActive(session: Session) {
if (!autoAcceptInvites.isEnabled) {
return
}
if (disposables.containsKey(session.sessionId)) {
return
}
session.addListener(this)
val roomQueryParams = roomSummaryQueryParams {
this.memberships = listOf(Membership.INVITE)
}
val rxSession = session.rx()
Observable
.combineLatest(
rxSession.liveRoomSummaries(roomQueryParams),
rxSession.liveRoomChangeMembershipState().debounce(1, TimeUnit.SECONDS),
{ invitedRooms, _ -> invitedRooms.map { it.roomId } }
)
.filter { it.isNotEmpty() }
.subscribe { invitedRoomIds ->
session.coroutineScope.launch {
semaphore.withPermit {
Timber.v("Invited roomIds: $invitedRoomIds")
for (roomId in invitedRoomIds) {
async { session.joinRoomSafely(roomId) }.start()
}
}
}
}
.also {
disposables[session.sessionId] = it
}
}
private suspend fun Session.joinRoomSafely(roomId: String) {
val roomMembershipChanged = getChangeMemberships(roomId)
if (roomMembershipChanged != ChangeMembershipState.Joined && !roomMembershipChanged.isInProgress()) {
try {
Timber.v("Try auto join room: $roomId")
joinRoom(roomId)
} catch (failure: Throwable) {
Timber.v("Failed auto join room: $roomId")
}
}
}
override fun onSessionStopped(session: Session) {
session.removeListener(this)
disposables.remove(session.sessionId)?.dispose()
}
}

View File

@ -27,6 +27,7 @@ import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.FirstThrottler import im.vector.app.core.utils.FirstThrottler
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import me.gujun.android.span.span import me.gujun.android.span.span
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
@ -50,7 +51,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
private val activeSessionDataSource: ActiveSessionDataSource, private val activeSessionDataSource: ActiveSessionDataSource,
private val iconLoader: IconLoader, private val iconLoader: IconLoader,
private val bitmapLoader: BitmapLoader, private val bitmapLoader: BitmapLoader,
private val outdatedDetector: OutdatedEventDetector?) { private val outdatedDetector: OutdatedEventDetector?,
private val autoAcceptInvites: AutoAcceptInvites) {
private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY)
private var backgroundHandler: Handler private var backgroundHandler: Handler
@ -254,7 +256,12 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
} }
} }
is InviteNotifiableEvent -> { is InviteNotifiableEvent -> {
//invitationEvents.add(event) if(autoAcceptInvites.hideInvites){
// Forget this event
eventIterator.remove()
}else {
invitationEvents.add(event)
}
} }
is SimpleNotifiableEvent -> simpleEvents.add(event) is SimpleNotifiableEvent -> simpleEvents.add(event)
else -> Timber.w("Type not handled") else -> Timber.w("Type not handled")

View File

@ -28,6 +28,7 @@ import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.AppStateHandler
import im.vector.app.RoomGroupingMethod import im.vector.app.RoomGroupingMethod
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.group import im.vector.app.group
import im.vector.app.space import im.vector.app.space
@ -54,7 +55,8 @@ import java.util.concurrent.TimeUnit
class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState,
private val appStateHandler: AppStateHandler, private val appStateHandler: AppStateHandler,
private val session: Session, private val session: Session,
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences,
private val autoAcceptInvites: AutoAcceptInvites
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) { ) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {
@AssistedFactory @AssistedFactory
@ -119,7 +121,13 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
.throttleFirst(300, TimeUnit.MILLISECONDS) .throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.computation()) .observeOn(Schedulers.computation())
.subscribe { .subscribe {
val inviteCount = 0 val inviteCount = if(autoAcceptInvites.hideInvites){
0
}else {
session.getRoomSummaries(
roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) }
).size
}
val totalCount = session.getNotificationCountForRooms( val totalCount = session.getNotificationCountForRooms(
roomSummaryQueryParams { roomSummaryQueryParams {
this.memberships = listOf(Membership.JOIN) this.memberships = listOf(Membership.JOIN)