From 646f00f3fcbdc9d06d149d772c41e1b14fee1dc1 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 17 Jun 2021 20:49:08 +0200 Subject: [PATCH 1/4] First test for auto accept invite [WIP] --- .../java/im/vector/app/AppStateHandler.kt | 61 ++++++++++++++++--- .../features/call/lookup/CallUserMapper.kt | 4 -- .../app/features/home/HomeDetailViewModel.kt | 17 +----- .../home/UnreadMessagesSharedViewModel.kt | 11 +--- .../room/list/GroupRoomListSectionBuilder.kt | 19 ------ .../room/list/SpaceRoomListSectionBuilder.kt | 37 ----------- .../NotificationDrawerManager.kt | 4 +- .../features/spaces/SpacesListViewModel.kt | 5 +- 8 files changed, 59 insertions(+), 99 deletions(-) diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index c5eac7e3e0..eb60a66436 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -24,13 +24,20 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.BehaviorDataSource import im.vector.app.features.session.coroutineScope import im.vector.app.features.ui.UiStateRepository +import io.reactivex.Observable import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull 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.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.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 @@ -49,19 +56,22 @@ fun RoomGroupingMethod.group() = (this as? RoomGroupingMethod.ByLegacyGroup)?.gr // TODO Keep this class for now, will maybe be used fro Space @Singleton class AppStateHandler @Inject constructor( - sessionDataSource: ActiveSessionDataSource, + private val sessionDataSource: ActiveSessionDataSource, private val uiStateRepository: UiStateRepository, private val activeSessionHolder: ActiveSessionHolder ) : LifecycleObserver { private val compositeDisposable = CompositeDisposable() - private val selectedSpaceDataSource = BehaviorDataSource>(Option.empty()) val selectedRoomGroupingObservable = selectedSpaceDataSource.observe() fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? = selectedSpaceDataSource.currentValue?.orNull() + init { + observeActiveSession() + } + fun setCurrentSpace(spaceId: String?, session: Session? = null) { val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.BySpace @@ -92,12 +102,13 @@ class AppStateHandler @Inject constructor( } } - init { + private fun observeActiveSession(){ sessionDataSource.observe() .distinctUntilChanged() .subscribe { - // sessionDataSource could already return a session while acitveSession holder still returns null + // sessionDataSource could already return a session while activeSession holder still returns null it.orNull()?.let { session -> + observeInvitesForAutoAccept(session) if (uiStateRepository.isGroupingMethodSpace(session.sessionId)) { setCurrentSpace(uiStateRepository.getSelectedSpace(session.sessionId), session) } else { @@ -109,6 +120,41 @@ 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() + 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? { return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId } @@ -117,16 +163,11 @@ class AppStateHandler @Inject constructor( return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.ByLegacyGroup)?.groupSummary?.groupId } - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun entersForeground() { - } - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun entersBackground() { - compositeDisposable.clear() val session = activeSessionHolder.getSafeActiveSession() ?: return when (val currentMethod = selectedSpaceDataSource.currentValue?.orNull() ?: RoomGroupingMethod.BySpace(null)) { - is RoomGroupingMethod.BySpace -> { + is RoomGroupingMethod.BySpace -> { uiStateRepository.storeGroupingMethod(true, session.sessionId) uiStateRepository.storeSelectedSpace(currentMethod.spaceSummary?.roomId, session.sessionId) } diff --git a/vector/src/main/java/im/vector/app/features/call/lookup/CallUserMapper.kt b/vector/src/main/java/im/vector/app/features/call/lookup/CallUserMapper.kt index 04177bd2b0..243f1ab6fb 100644 --- a/vector/src/main/java/im/vector/app/features/call/lookup/CallUserMapper.kt +++ b/vector/src/main/java/im/vector/app/features/call/lookup/CallUserMapper.kt @@ -57,10 +57,6 @@ class CallUserMapper(private val session: Session, private val protocolsChecker: // will make sure we know where how to map calls and also allow us know not to display // it in the future. invitedRoom.markVirtual(nativeRoomId) - // also auto-join the virtual room if we have a matching native room - // (possibly we should only join if we've also joined the native room, then we'd also have - // to make sure we joined virtual rooms on joining a native one) - session.joinRoom(invitedRoomId) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index b6210ae019..d636259512 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -204,21 +204,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } is RoomGroupingMethod.BySpace -> { val activeSpaceRoomId = groupingMethod.spaceSummary?.roomId - val dmInvites = session.getRoomSummaries( - roomSummaryQueryParams { - memberships = listOf(Membership.INVITE) - roomCategoryFilter = RoomCategoryFilter.ONLY_DM - activeSpaceFilter = activeSpaceRoomId?.let { ActiveSpaceFilter.ActiveSpace(it) } ?: ActiveSpaceFilter.None - } - ).size - - val roomsInvite = session.getRoomSummaries( - roomSummaryQueryParams { - memberships = listOf(Membership.INVITE) - roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(groupingMethod.spaceSummary?.roomId) - } - ).size + val dmInvites = 0 + val roomsInvite = 0 val dmRooms = session.getNotificationCountForRooms( roomSummaryQueryParams { diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt index 9b506b6ed7..44a2f8cf09 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -92,12 +92,7 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia this.activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null) } ) - val invites = session.getRoomSummaries( - roomSummaryQueryParams { - this.memberships = listOf(Membership.INVITE) - this.activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null) - } - ).size + val invites = 0 copy( homeSpaceUnread = RoomAggregateNotificationCount( counts.notificationCount + invites, @@ -129,9 +124,7 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia is RoomGroupingMethod.BySpace -> { val selectedSpace = appStateHandler.safeActiveSpaceId() - val inviteCount = session.getRoomSummaries( - roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } - ).size + val inviteCount = 0 val totalCount = session.getNotificationCountForRooms( roomSummaryQueryParams { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt index 22b0eb091c..62b0612d4b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt @@ -115,16 +115,6 @@ class GroupRoomListSectionBuilder( private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList, actualGroupId: String?) { - addSection( - sections, - activeSpaceAwareQueries, - R.string.invitations_header, - true - ) { - it.memberships = listOf(Membership.INVITE) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - it.activeGroupId = actualGroupId - } addSection( sections, @@ -180,15 +170,6 @@ class GroupRoomListSectionBuilder( activeSpaceAwareQueries: MutableList, actualGroupId: String? ) { - addSection(sections, - activeSpaceAwareQueries, - R.string.invitations_header, - true - ) { - it.memberships = listOf(Membership.INVITE) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM - it.activeGroupId = actualGroupId - } addSection( sections, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt index 266adf6b0c..e1b205eca2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt @@ -88,21 +88,6 @@ class SpaceRoomListSectionBuilder( ) } RoomListDisplayMode.NOTIFICATIONS -> { - 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( sections = sections, @@ -136,18 +121,6 @@ class SpaceRoomListSectionBuilder( } private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { - 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( sections, activeSpaceAwareQueries, @@ -253,16 +226,6 @@ class SpaceRoomListSectionBuilder( } private fun buildDmSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { - 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, activeSpaceAwareQueries, diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index 7ac9b28b9a..cf49b5ce0f 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -253,7 +253,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context roomEvents.add(event) } } - is InviteNotifiableEvent -> invitationEvents.add(event) + is InviteNotifiableEvent -> { + //invitationEvents.add(event) + } is SimpleNotifiableEvent -> simpleEvents.add(event) else -> Timber.w("Type not handled") } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index d7d7af4ca5..c77a0f36c9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -119,10 +119,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp .throttleFirst(300, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) .subscribe { - val inviteCount = session.getRoomSummaries( - roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } - ).size - + val inviteCount = 0 val totalCount = session.getNotificationCountForRooms( roomSummaryQueryParams { this.memberships = listOf(Membership.JOIN) From 6b10406622e3cbc1022538664e70cc81f50ee9ed Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 18 Jun 2021 17:24:51 +0200 Subject: [PATCH 2/4] AutoAcceptInvite: refact and hide behind flag --- .../sdk/api/session/room/RoomService.kt | 6 ++ .../sdk/internal/session/SessionModule.kt | 3 + .../session/room/DefaultRoomService.kt | 4 + .../RoomChangeMembershipStateDataSource.kt | 3 +- .../room/membership/joining/JoinRoomTask.kt | 4 + .../java/im/vector/app/AppStateHandler.kt | 50 +--------- .../im/vector/app/core/di/ScreenComponent.kt | 2 + .../im/vector/app/core/di/VectorComponent.kt | 3 + .../im/vector/app/core/di/VectorModule.kt | 5 + .../app/features/home/HomeDetailViewModel.kt | 25 ++++- .../home/UnreadMessagesSharedViewModel.kt | 25 ++++- .../room/list/GroupRoomListSectionBuilder.kt | 52 +++++++--- .../home/room/list/RoomListViewModel.kt | 6 +- .../room/list/RoomListViewModelFactory.kt | 15 +-- .../room/list/SpaceRoomListSectionBuilder.kt | 55 ++++++++++- .../app/features/invite/AutoAcceptInvites.kt | 29 ++++++ .../app/features/invite/InvitesAcceptor.kt | 97 +++++++++++++++++++ .../NotificationDrawerManager.kt | 11 ++- .../features/spaces/SpacesListViewModel.kt | 12 ++- 19 files changed, 325 insertions(+), 82 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt create mode 100644 vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index 871c5378a6..b7377df1b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -125,6 +125,12 @@ interface RoomService { */ 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. * It allows you to track this in your client to known what is currently being processed by the SDK. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index e6da21315b..c750cb1f73 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -386,4 +386,7 @@ internal abstract class SessionModule { @Binds abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor + + + } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index d9fe1288e2..632ea4c450 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -134,6 +134,10 @@ internal class DefaultRoomService @Inject constructor( deleteRoomAliasTask.execute(DeleteRoomAliasTask.Params(roomAlias)) } + override fun getChangeMemberships(roomIdOrAlias: String): ChangeMembershipState { + return roomChangeMembershipStateDataSource.getState(roomIdOrAlias) + } + override fun getChangeMembershipsLive(): LiveData> { return roomChangeMembershipStateDataSource.getLiveStates() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomChangeMembershipStateDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomChangeMembershipStateDataSource.kt index b9c547d4fb..35d8cb08af 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomChangeMembershipStateDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomChangeMembershipStateDataSource.kt @@ -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.model.Membership import org.matrix.android.sdk.internal.session.SessionScope +import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject /** @@ -30,7 +31,7 @@ import javax.inject.Inject internal class RoomChangeMembershipStateDataSource @Inject constructor() { private val mutableLiveStates = MutableLiveData>(emptyMap()) - private val states = HashMap() + private val states = ConcurrentHashMap() /** * This will update local states to be synced with the server. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt index 33776e4f6e..562b25683b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt @@ -54,6 +54,10 @@ internal class DefaultJoinRoomTask @Inject constructor( ) : JoinRoomTask { 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) val joinRoomResponse = try { executeRequest(globalErrorReceiver) { diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index eb60a66436..3822bd6c08 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -22,22 +22,16 @@ import androidx.lifecycle.OnLifecycleEvent import arrow.core.Option import im.vector.app.core.di.ActiveSessionHolder 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.ui.UiStateRepository -import io.reactivex.Observable import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull 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.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.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 @@ -58,7 +52,8 @@ fun RoomGroupingMethod.group() = (this as? RoomGroupingMethod.ByLegacyGroup)?.gr class AppStateHandler @Inject constructor( private val sessionDataSource: ActiveSessionDataSource, private val uiStateRepository: UiStateRepository, - private val activeSessionHolder: ActiveSessionHolder + private val activeSessionHolder: ActiveSessionHolder, + private val invitesAcceptor: InvitesAcceptor ) : LifecycleObserver { private val compositeDisposable = CompositeDisposable() @@ -102,13 +97,13 @@ class AppStateHandler @Inject constructor( } } - private fun observeActiveSession(){ + private fun observeActiveSession() { sessionDataSource.observe() .distinctUntilChanged() .subscribe { // sessionDataSource could already return a session while activeSession holder still returns null it.orNull()?.let { session -> - observeInvitesForAutoAccept(session) + invitesAcceptor.onSessionActive(session) if (uiStateRepository.isGroupingMethodSpace(session.sessionId)) { setCurrentSpace(uiStateRepository.getSelectedSpace(session.sessionId), session) } 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() - 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? { return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId } diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index 3c11bfcd13..38edb771bb 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -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.list.RoomListModule 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.VectorInviteView import im.vector.app.features.link.LinkHandlerActivity @@ -122,6 +123,7 @@ interface ScreenComponent { fun errorFormatter(): ErrorFormatter fun uiStateRepository(): UiStateRepository fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog + fun autoAcceptInvites(): AutoAcceptInvites /* ========================================================================================== * Activities diff --git a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt index e5a47e872c..4a3379cb5a 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt @@ -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.html.EventHtmlRenderer 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.navigation.Navigator import im.vector.app.features.notifications.NotifiableEventResolver @@ -160,6 +161,8 @@ interface VectorComponent { fun pinLocker(): PinLocker + fun autoAcceptInvites(): AutoAcceptInvites + fun webRtcCallManager(): WebRtcCallManager fun roomSummaryHolder(): RoomSummariesHolder diff --git a/vector/src/main/java/im/vector/app/core/di/VectorModule.kt b/vector/src/main/java/im/vector/app/core/di/VectorModule.kt index 77cad0ae73..006a2f5aa0 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/VectorModule.kt @@ -25,6 +25,8 @@ import dagger.Module import dagger.Provides import im.vector.app.core.error.DefaultErrorFormatter 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.Navigator import im.vector.app.features.pin.PinCodeStore @@ -105,4 +107,7 @@ abstract class VectorModule { @Binds abstract fun bindPinCodeStore(store: SharedPrefPinCodeStore): PinCodeStore + + @Binds + abstract fun bindAutoAcceptInvites(autoAcceptInvites: CompileTimeAutoAcceptInvites): AutoAcceptInvites } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index d636259512..f282a3137c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -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.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper +import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers @@ -56,7 +57,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private val uiStateRepository: UiStateRepository, private val callManager: WebRtcCallManager, private val directRoomHelper: DirectRoomHelper, - private val appStateHandler: AppStateHandler) + private val appStateHandler: AppStateHandler, +private val autoAcceptInvites: AutoAcceptInvites) : VectorViewModel(initialState), CallProtocolsChecker.Listener { @@ -204,8 +206,25 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } is RoomGroupingMethod.BySpace -> { val activeSpaceRoomId = groupingMethod.spaceSummary?.roomId - val dmInvites = 0 - val roomsInvite = 0 + var dmInvites = 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( roomSummaryQueryParams { diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt index 44a2f8cf09..e9e2447b39 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -29,6 +29,7 @@ import im.vector.app.RoomGroupingMethod import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences import io.reactivex.Observable import io.reactivex.schedulers.Schedulers @@ -54,7 +55,8 @@ data class CountInfo( class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initialState: UnreadMessagesState, session: Session, private val vectorPreferences: VectorPreferences, - appStateHandler: AppStateHandler) + appStateHandler: AppStateHandler, + private val autoAcceptInvites: AutoAcceptInvites) : VectorViewModel(initialState) { @AssistedFactory @@ -92,7 +94,17 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia 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( homeSpaceUnread = RoomAggregateNotificationCount( counts.notificationCount + invites, @@ -124,8 +136,13 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia is RoomGroupingMethod.BySpace -> { 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( roomSummaryQueryParams { this.memberships = listOf(Membership.JOIN) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt index 62b0612d4b..a23251510f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt @@ -22,6 +22,7 @@ import im.vector.app.R import im.vector.app.RoomGroupingMethod import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.RoomListDisplayMode +import im.vector.app.features.invite.AutoAcceptInvites import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope @@ -38,6 +39,7 @@ class GroupRoomListSectionBuilder( val stringProvider: StringProvider, val viewModelScope: CoroutineScope, val appStateHandler: AppStateHandler, + private val autoAcceptInvites: AutoAcceptInvites, val onDisposable: (Disposable) -> Unit, val onUdpatable: (UpdatableLivePageResult) -> Unit ) : RoomListSectionBuilder { @@ -48,15 +50,15 @@ class GroupRoomListSectionBuilder( val actualGroupId = appStateHandler.safeActiveGroupId() when (mode) { - RoomListDisplayMode.PEOPLE -> { + RoomListDisplayMode.PEOPLE -> { // 3 sections Invites / Fav / Dms buildPeopleSections(sections, activeGroupAwareQueries, actualGroupId) } - RoomListDisplayMode.ROOMS -> { + RoomListDisplayMode.ROOMS -> { // 5 sections invites / Fav / Rooms / Low Priority / Server notice buildRoomsSections(sections, activeGroupAwareQueries, actualGroupId) } - RoomListDisplayMode.FILTERED -> { + RoomListDisplayMode.FILTERED -> { // Used when searching for rooms withQueryParams( { @@ -73,17 +75,18 @@ class GroupRoomListSectionBuilder( ) } RoomListDisplayMode.NOTIFICATIONS -> { - addSection( - sections, - activeGroupAwareQueries, - R.string.invitations_header, - true - ) { - it.memberships = listOf(Membership.INVITE) - it.roomCategoryFilter = RoomCategoryFilter.ALL - it.activeGroupId = actualGroupId + if (!autoAcceptInvites.hideInvites) { + addSection( + sections, + activeGroupAwareQueries, + R.string.invitations_header, + true + ) { + it.memberships = listOf(Membership.INVITE) + it.roomCategoryFilter = RoomCategoryFilter.ALL + it.activeGroupId = actualGroupId + } } - addSection( sections, activeGroupAwareQueries, @@ -115,6 +118,18 @@ class GroupRoomListSectionBuilder( private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList, 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( sections, @@ -170,6 +185,17 @@ class GroupRoomListSectionBuilder( activeSpaceAwareQueries: MutableList, 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( sections, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index fbb8faebb0..c5f166ea5b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -30,6 +30,7 @@ import im.vector.app.RoomGroupingMethod import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -49,7 +50,8 @@ class RoomListViewModel @Inject constructor( private val session: Session, private val stringProvider: StringProvider, private val appStateHandler: AppStateHandler, - private val vectorPreferences: VectorPreferences + private val vectorPreferences: VectorPreferences, + private val autoAcceptInvites: AutoAcceptInvites ) : VectorViewModel(initialState) { interface Factory { @@ -126,6 +128,7 @@ class RoomListViewModel @Inject constructor( appStateHandler, viewModelScope, suggestedRoomJoiningState, + autoAcceptInvites, { it.disposeOnClear() }, @@ -140,6 +143,7 @@ class RoomListViewModel @Inject constructor( stringProvider, viewModelScope, appStateHandler, + autoAcceptInvites, { it.disposeOnClear() }, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt index a30c175f41..6b269356c7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.list import im.vector.app.AppStateHandler import im.vector.app.core.resources.StringProvider +import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences import org.matrix.android.sdk.api.session.Session import javax.inject.Inject @@ -26,16 +27,18 @@ import javax.inject.Provider class RoomListViewModelFactory @Inject constructor(private val session: Provider, private val appStateHandler: AppStateHandler, private val stringProvider: StringProvider, - private val vectorPreferences: VectorPreferences) + private val vectorPreferences: VectorPreferences, + private val autoAcceptInvites: AutoAcceptInvites) : RoomListViewModel.Factory { override fun create(initialState: RoomListViewState): RoomListViewModel { return RoomListViewModel( - initialState, - session.get(), - stringProvider, - appStateHandler, - vectorPreferences + initialState = initialState, + session = session.get(), + stringProvider = stringProvider, + appStateHandler = appStateHandler, + vectorPreferences = vectorPreferences, + autoAcceptInvites = autoAcceptInvites ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt index e1b205eca2..7d3816af26 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt @@ -26,6 +26,7 @@ import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.RoomListDisplayMode +import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.space import io.reactivex.Observable import io.reactivex.disposables.Disposable @@ -50,6 +51,7 @@ class SpaceRoomListSectionBuilder( val appStateHandler: AppStateHandler, val viewModelScope: CoroutineScope, private val suggestedRoomJoiningState: LiveData>>, + private val autoAcceptInvites: AutoAcceptInvites, val onDisposable: (Disposable) -> Unit, val onUdpatable: (UpdatableLivePageResult) -> Unit, val onlyOrphansInHome: Boolean = false @@ -66,13 +68,13 @@ class SpaceRoomListSectionBuilder( val sections = mutableListOf() val activeSpaceAwareQueries = mutableListOf() when (mode) { - RoomListDisplayMode.PEOPLE -> { + RoomListDisplayMode.PEOPLE -> { buildDmSections(sections, activeSpaceAwareQueries) } - RoomListDisplayMode.ROOMS -> { + RoomListDisplayMode.ROOMS -> { buildRoomsSections(sections, activeSpaceAwareQueries) } - RoomListDisplayMode.FILTERED -> { + RoomListDisplayMode.FILTERED -> { withQueryParams( { it.memberships = Membership.activeMemberships() @@ -88,6 +90,23 @@ class SpaceRoomListSectionBuilder( ) } 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( sections = sections, @@ -121,6 +140,20 @@ class SpaceRoomListSectionBuilder( } private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { + 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( sections, activeSpaceAwareQueries, @@ -226,6 +259,18 @@ class SpaceRoomListSectionBuilder( } private fun buildDmSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { + 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, activeSpaceAwareQueries, @@ -350,7 +395,7 @@ class SpaceRoomListSectionBuilder( activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(currentSpace) ) } - RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL -> { + RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL -> { if (currentSpace == null) { copy( activeSpaceFilter = ActiveSpaceFilter.None @@ -361,7 +406,7 @@ class SpaceRoomListSectionBuilder( ) } } - RoomListViewModel.SpaceFilterStrategy.NONE -> this + RoomListViewModel.SpaceFilterStrategy.NONE -> this } } } diff --git a/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt b/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt new file mode 100644 index 0000000000..4027ba23d5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt @@ -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 +} diff --git a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt new file mode 100644 index 0000000000..f084eb9bb4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt @@ -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() + 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() + } +} diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index cf49b5ce0f..c051a866b5 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -27,6 +27,7 @@ import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.FirstThrottler +import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences import me.gujun.android.span.span 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 iconLoader: IconLoader, 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 var backgroundHandler: Handler @@ -254,7 +256,12 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } } is InviteNotifiableEvent -> { - //invitationEvents.add(event) + if(autoAcceptInvites.hideInvites){ + // Forget this event + eventIterator.remove() + }else { + invitationEvents.add(event) + } } is SimpleNotifiableEvent -> simpleEvents.add(event) else -> Timber.w("Type not handled") diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index c77a0f36c9..93205364f2 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -28,6 +28,7 @@ import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.RoomGroupingMethod 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.group import im.vector.app.space @@ -54,7 +55,8 @@ import java.util.concurrent.TimeUnit class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, private val appStateHandler: AppStateHandler, private val session: Session, - private val vectorPreferences: VectorPreferences + private val vectorPreferences: VectorPreferences, + private val autoAcceptInvites: AutoAcceptInvites ) : VectorViewModel(initialState) { @AssistedFactory @@ -119,7 +121,13 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp .throttleFirst(300, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) .subscribe { - val inviteCount = 0 + val inviteCount = if(autoAcceptInvites.hideInvites){ + 0 + }else { + session.getRoomSummaries( + roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } + ).size + } val totalCount = session.getNotificationCountForRooms( roomSummaryQueryParams { this.memberships = listOf(Membership.JOIN) From c551cf305827e70722cbe98a0755244d3fa707b8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 18 Jun 2021 17:30:32 +0200 Subject: [PATCH 3/4] Clean and add towncrier --- .../org/matrix/android/sdk/internal/session/SessionModule.kt | 3 --- newsfragment/3531.feature | 1 + .../java/im/vector/app/features/home/HomeDetailViewModel.kt | 2 +- .../java/im/vector/app/features/invite/AutoAcceptInvites.kt | 4 ++-- .../app/features/notifications/NotificationDrawerManager.kt | 4 ++-- .../java/im/vector/app/features/spaces/SpacesListViewModel.kt | 4 ++-- 6 files changed, 8 insertions(+), 10 deletions(-) create mode 100644 newsfragment/3531.feature diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index c750cb1f73..e6da21315b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -386,7 +386,4 @@ internal abstract class SessionModule { @Binds abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor - - - } diff --git a/newsfragment/3531.feature b/newsfragment/3531.feature new file mode 100644 index 0000000000..e8b63584e3 --- /dev/null +++ b/newsfragment/3531.feature @@ -0,0 +1 @@ +Introduces AutoAcceptInvites which can be enabled at compile time. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index f282a3137c..3614b345c6 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -208,7 +208,7 @@ private val autoAcceptInvites: AutoAcceptInvites) val activeSpaceRoomId = groupingMethod.spaceSummary?.roomId var dmInvites = 0 var roomsInvite = 0 - if(!autoAcceptInvites.hideInvites) { + if (!autoAcceptInvites.hideInvites) { dmInvites = session.getRoomSummaries( roomSummaryQueryParams { memberships = listOf(Membership.INVITE) diff --git a/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt b/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt index 4027ba23d5..4f39ee0925 100644 --- a/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt +++ b/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt @@ -24,6 +24,6 @@ interface AutoAcceptInvites { } class CompileTimeAutoAcceptInvites @Inject constructor() : AutoAcceptInvites { - override val isEnabled = true - override val hideInvites = false + override val isEnabled = false + override val hideInvites = isEnabled } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index c051a866b5..37ed1e654a 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -256,10 +256,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } } is InviteNotifiableEvent -> { - if(autoAcceptInvites.hideInvites){ + if (autoAcceptInvites.hideInvites) { // Forget this event eventIterator.remove() - }else { + } else { invitationEvents.add(event) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index 93205364f2..ca1dc54cf5 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -121,9 +121,9 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp .throttleFirst(300, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) .subscribe { - val inviteCount = if(autoAcceptInvites.hideInvites){ + val inviteCount = if (autoAcceptInvites.hideInvites) { 0 - }else { + } else { session.getRoomSummaries( roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } ).size From 48fa9e1a5e83469058eb69de6492da4a8a1cde95 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 21 Jun 2021 17:57:47 +0200 Subject: [PATCH 4/4] Clean after benoits review --- .../java/im/vector/app/AppStateHandler.kt | 15 +++-- .../java/im/vector/app/VectorApplication.kt | 3 + .../app/features/home/HomeDetailViewModel.kt | 3 +- .../room/list/GroupRoomListSectionBuilder.kt | 7 ++- .../room/list/SpaceRoomListSectionBuilder.kt | 7 ++- .../app/features/invite/AutoAcceptInvites.kt | 18 +++++- .../app/features/invite/InvitesAcceptor.kt | 58 +++++++++++++++++-- 7 files changed, 89 insertions(+), 22 deletions(-) diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 3822bd6c08..06174b9573 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -22,7 +22,6 @@ import androidx.lifecycle.OnLifecycleEvent import arrow.core.Option import im.vector.app.core.di.ActiveSessionHolder 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.ui.UiStateRepository import io.reactivex.disposables.CompositeDisposable @@ -52,8 +51,7 @@ fun RoomGroupingMethod.group() = (this as? RoomGroupingMethod.ByLegacyGroup)?.gr class AppStateHandler @Inject constructor( private val sessionDataSource: ActiveSessionDataSource, private val uiStateRepository: UiStateRepository, - private val activeSessionHolder: ActiveSessionHolder, - private val invitesAcceptor: InvitesAcceptor + private val activeSessionHolder: ActiveSessionHolder ) : LifecycleObserver { private val compositeDisposable = CompositeDisposable() @@ -63,10 +61,6 @@ class AppStateHandler @Inject constructor( fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? = selectedSpaceDataSource.currentValue?.orNull() - init { - observeActiveSession() - } - fun setCurrentSpace(spaceId: String?, session: Session? = null) { val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.BySpace @@ -103,7 +97,6 @@ class AppStateHandler @Inject constructor( .subscribe { // sessionDataSource could already return a session while activeSession holder still returns null it.orNull()?.let { session -> - invitesAcceptor.onSessionActive(session) if (uiStateRepository.isGroupingMethodSpace(session.sessionId)) { setCurrentSpace(uiStateRepository.getSelectedSpace(session.sessionId), session) } else { @@ -123,8 +116,14 @@ class AppStateHandler @Inject constructor( return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.ByLegacyGroup)?.groupSummary?.groupId } + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun entersForeground() { + observeActiveSession() + } + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun entersBackground() { + compositeDisposable.clear() val session = activeSessionHolder.getSafeActiveSession() ?: return when (val currentMethod = selectedSpaceDataSource.currentValue?.orNull() ?: RoomGroupingMethod.BySpace(null)) { is RoomGroupingMethod.BySpace -> { diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index f3e2f8740e..4791c2e499 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -47,6 +47,7 @@ import im.vector.app.core.rx.RxConfig import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.disclaimer.doNotShowDisclaimerDialog +import im.vector.app.features.invite.InvitesAcceptor import im.vector.app.features.lifecycle.VectorActivityLifecycleCallbacks import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationUtils @@ -95,6 +96,7 @@ class VectorApplication : @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var pinLocker: PinLocker @Inject lateinit var callManager: WebRtcCallManager + @Inject lateinit var invitesAcceptor: InvitesAcceptor lateinit var vectorComponent: VectorComponent @@ -116,6 +118,7 @@ class VectorApplication : appContext = this vectorComponent = DaggerVectorComponent.factory().create(this) vectorComponent.inject(this) + invitesAcceptor.initialize() vectorUncaughtExceptionHandler.activate(this) rxConfig.setupRxPlugin() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 3614b345c6..b960402f90 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -32,6 +32,7 @@ import im.vector.app.features.call.lookup.CallProtocolsChecker import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.invite.AutoAcceptInvites +import im.vector.app.features.invite.showInvites import im.vector.app.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers @@ -208,7 +209,7 @@ private val autoAcceptInvites: AutoAcceptInvites) val activeSpaceRoomId = groupingMethod.spaceSummary?.roomId var dmInvites = 0 var roomsInvite = 0 - if (!autoAcceptInvites.hideInvites) { + if (autoAcceptInvites.showInvites()) { dmInvites = session.getRoomSummaries( roomSummaryQueryParams { memberships = listOf(Membership.INVITE) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt index a23251510f..106a02cd3c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt @@ -23,6 +23,7 @@ import im.vector.app.RoomGroupingMethod import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.invite.AutoAcceptInvites +import im.vector.app.features.invite.showInvites import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope @@ -75,7 +76,7 @@ class GroupRoomListSectionBuilder( ) } RoomListDisplayMode.NOTIFICATIONS -> { - if (!autoAcceptInvites.hideInvites) { + if (autoAcceptInvites.showInvites()) { addSection( sections, activeGroupAwareQueries, @@ -118,7 +119,7 @@ class GroupRoomListSectionBuilder( private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList, actualGroupId: String?) { - if (!autoAcceptInvites.hideInvites) { + if (autoAcceptInvites.showInvites()) { addSection( sections, activeSpaceAwareQueries, @@ -185,7 +186,7 @@ class GroupRoomListSectionBuilder( activeSpaceAwareQueries: MutableList, actualGroupId: String? ) { - if (!autoAcceptInvites.hideInvites) { + if (autoAcceptInvites.showInvites()) { addSection(sections, activeSpaceAwareQueries, R.string.invitations_header, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt index 7d3816af26..5a296ce7ed 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt @@ -27,6 +27,7 @@ import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.invite.AutoAcceptInvites +import im.vector.app.features.invite.showInvites import im.vector.app.space import io.reactivex.Observable import io.reactivex.disposables.Disposable @@ -90,7 +91,7 @@ class SpaceRoomListSectionBuilder( ) } RoomListDisplayMode.NOTIFICATIONS -> { - if (!autoAcceptInvites.hideInvites) { + if (autoAcceptInvites.showInvites()) { addSection( sections = sections, activeSpaceUpdaters = activeSpaceAwareQueries, @@ -140,7 +141,7 @@ class SpaceRoomListSectionBuilder( } private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { - if (!autoAcceptInvites.hideInvites) { + if (autoAcceptInvites.showInvites()) { addSection( sections = sections, activeSpaceUpdaters = activeSpaceAwareQueries, @@ -259,7 +260,7 @@ class SpaceRoomListSectionBuilder( } private fun buildDmSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { - if (!autoAcceptInvites.hideInvites) { + if (autoAcceptInvites.showInvites()) { addSection(sections = sections, activeSpaceUpdaters = activeSpaceAwareQueries, nameRes = R.string.invitations_header, diff --git a/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt b/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt index 4f39ee0925..87febb37bc 100644 --- a/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt +++ b/vector/src/main/java/im/vector/app/features/invite/AutoAcceptInvites.kt @@ -18,12 +18,28 @@ package im.vector.app.features.invite import javax.inject.Inject +/** + * This interface defines 2 flags so you can handle auto accept invites. + * At the moment we only have [CompileTimeAutoAcceptInvites] implementation. + */ interface AutoAcceptInvites { + /** + * Enable auto-accept invites. It means, as soon as you got an invite from the sync, it will try to join it. + */ val isEnabled: Boolean + + /** + * Hide invites from the UI (from notifications, notification count and room list). By default invites are hidden when [isEnabled] is true + */ val hideInvites: Boolean + get() = isEnabled } +fun AutoAcceptInvites.showInvites() = !hideInvites + +/** + * Simple compile time implementation of AutoAcceptInvites flags. + */ class CompileTimeAutoAcceptInvites @Inject constructor() : AutoAcceptInvites { override val isEnabled = false - override val hideInvites = isEnabled } diff --git a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt index f084eb9bb4..6e7de1c35b 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InvitesAcceptor.kt @@ -16,6 +16,7 @@ package im.vector.app.features.invite +import im.vector.app.ActiveSessionDataSource import im.vector.app.features.session.coroutineScope import io.reactivex.Observable import io.reactivex.disposables.Disposable @@ -23,7 +24,10 @@ import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.Room 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 @@ -39,16 +43,35 @@ import javax.inject.Singleton * This mechanism will be on only if AutoAcceptInvites.isEnabled is true. */ @Singleton -class InvitesAcceptor @Inject constructor(private val autoAcceptInvites: AutoAcceptInvites) : Session.Listener { +class InvitesAcceptor @Inject constructor( + private val sessionDataSource: ActiveSessionDataSource, + private val autoAcceptInvites: AutoAcceptInvites +) : Session.Listener { - private val disposables = HashMap() + private lateinit var activeSessionDisposable: Disposable + private val shouldRejectRoomIds = mutableSetOf() + private val invitedRoomDisposables = HashMap() private val semaphore = Semaphore(1) - fun onSessionActive(session: Session) { + fun initialize() { + observeActiveSession() + } + + private fun observeActiveSession() { + activeSessionDisposable = sessionDataSource.observe() + .distinctUntilChanged() + .subscribe { + it.orNull()?.let { session -> + onSessionActive(session) + } + } + } + + private fun onSessionActive(session: Session) { if (!autoAcceptInvites.isEnabled) { return } - if (disposables.containsKey(session.sessionId)) { + if (invitedRoomDisposables.containsKey(session.sessionId)) { return } session.addListener(this) @@ -74,11 +97,15 @@ class InvitesAcceptor @Inject constructor(private val autoAcceptInvites: AutoAcc } } .also { - disposables[session.sessionId] = it + invitedRoomDisposables[session.sessionId] = it } } private suspend fun Session.joinRoomSafely(roomId: String) { + if (shouldRejectRoomIds.contains(roomId)) { + getRoom(roomId)?.rejectInviteSafely() + return + } val roomMembershipChanged = getChangeMemberships(roomId) if (roomMembershipChanged != ChangeMembershipState.Joined && !roomMembershipChanged.isInProgress()) { try { @@ -86,12 +113,31 @@ class InvitesAcceptor @Inject constructor(private val autoAcceptInvites: AutoAcc joinRoom(roomId) } catch (failure: Throwable) { Timber.v("Failed auto join room: $roomId") + // if we got 404 on invites, the inviting user have left or the hs is off. + if (failure is Failure.ServerError && failure.httpCode == 404) { + val room = getRoom(roomId) ?: return + val inviterId = room.roomSummary()?.inviterId + // if the inviting user is on the same HS, there can only be one cause: they left, so we try to reject the invite. + if (inviterId?.endsWith(sessionParams.credentials.homeServer.orEmpty()).orFalse()) { + shouldRejectRoomIds.add(roomId) + room.rejectInviteSafely() + } + } } } } + private suspend fun Room.rejectInviteSafely() { + try { + leave(null) + shouldRejectRoomIds.remove(roomId) + } catch (failure: Throwable) { + Timber.v("Fail rejecting invite for room: $roomId") + } + } + override fun onSessionStopped(session: Session) { session.removeListener(this) - disposables.remove(session.sessionId)?.dispose() + invitedRoomDisposables.remove(session.sessionId)?.dispose() } }