Merge pull request #6598 from vector-im/task/eric/space-switching-unit-tests

Space Switching Refactoring and Unit Tests
This commit is contained in:
Eric Decanini 2022-07-29 16:21:33 +02:00 committed by GitHub
commit ddaf1128ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 395 additions and 108 deletions

1
changelog.d/6598.misc Normal file
View File

@ -0,0 +1 @@
Refactors SpaceStateHandler (previously AppStateHandler) and adds unit tests for it

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2022 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
import androidx.lifecycle.DefaultLifecycleObserver
import arrow.core.Option
import kotlinx.coroutines.flow.Flow
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.model.RoomSummary
/**
* Gets info about the current space the user has navigated to, any space backstack they may have
* and handles switching to different spaces
*/
interface SpaceStateHandler : DefaultLifecycleObserver {
/**
* Gets the current space the current user has navigated to
*
* @return null if the user is not in
*/
fun getCurrentSpace(): RoomSummary?
/**
* Sets the new space the current user is navigating to
*
* @param spaceId the id of the space being navigated to
* @param session the current active session
* @param persistNow if true, the current space will immediately be persisted in shared prefs
* @param isForwardNavigation whether this navigation is a forward action to properly handle backstack
*/
fun setCurrentSpace(
spaceId: String?,
session: Session? = null,
persistNow: Boolean = false,
isForwardNavigation: Boolean = true,
)
/**
* Gets the current backstack of spaces (via their id)
*
* null may be an entry in the ArrayDeque to indicate the root space (All Chats)
*/
fun getSpaceBackstack(): ArrayDeque<String?>
/**
* Gets a flow of the selected space for clients to react immediately to space changes
*/
fun getSelectedSpaceFlow(): Flow<Option<RoomSummary>>
/**
* Gets the id of the active space, or null if there is none
*/
fun getSafeActiveSpaceId(): String?
}

View File

@ -16,7 +16,6 @@
package im.vector.app package im.vector.app
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import arrow.core.Option import arrow.core.Option
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
@ -46,54 +45,60 @@ import javax.inject.Singleton
/** /**
* This class handles the global app state. * This class handles the global app state.
* It requires to be added to ProcessLifecycleOwner.get().lifecycle * It is required that this class is added as an observer to ProcessLifecycleOwner.get().lifecycle in [VectorApplication]
*/ */
// TODO Keep this class for now, will maybe be used fro Space
@Singleton @Singleton
class AppStateHandler @Inject constructor( class SpaceStateHandlerImpl @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 analyticsTracker: AnalyticsTracker private val analyticsTracker: AnalyticsTracker
) : DefaultLifecycleObserver { ) : SpaceStateHandler {
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private val selectedSpaceDataSource = BehaviorDataSource<Option<RoomSummary>>(Option.empty()) private val selectedSpaceDataSource = BehaviorDataSource<Option<RoomSummary>>(Option.empty())
private val selectedSpaceFlow = selectedSpaceDataSource.stream()
val selectedSpaceFlow = selectedSpaceDataSource.stream()
private val spaceBackstack = ArrayDeque<String?>() private val spaceBackstack = ArrayDeque<String?>()
fun getCurrentSpace(): RoomSummary? { override fun getCurrentSpace(): RoomSummary? {
return selectedSpaceDataSource.currentValue?.orNull()?.let { spaceSummary -> return selectedSpaceDataSource.currentValue?.orNull()?.let { spaceSummary ->
activeSessionHolder.getSafeActiveSession()?.roomService()?.getRoomSummary(spaceSummary.roomId) activeSessionHolder.getSafeActiveSession()?.roomService()?.getRoomSummary(spaceSummary.roomId)
} }
} }
fun setCurrentSpace(spaceId: String?, session: Session? = null, persistNow: Boolean = false, isForwardNavigation: Boolean = true) { override fun setCurrentSpace(
spaceId: String?,
session: Session?,
persistNow: Boolean,
isForwardNavigation: Boolean,
) {
val activeSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
val currentSpace = selectedSpaceDataSource.currentValue?.orNull() val currentSpace = selectedSpaceDataSource.currentValue?.orNull()
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return val spaceSummary = spaceId?.let { activeSession.getRoomSummary(spaceId) }
if (currentSpace != null && spaceId == currentSpace.roomId) return val sameSpaceSelected = currentSpace != null && spaceId == currentSpace.roomId
val spaceSum = spaceId?.let { uSession.getRoomSummary(spaceId) }
if (sameSpaceSelected) {
return
}
if (isForwardNavigation) { if (isForwardNavigation) {
spaceBackstack.addLast(currentSpace?.roomId) spaceBackstack.addLast(currentSpace?.roomId)
} }
if (persistNow) { if (persistNow) {
uiStateRepository.storeSelectedSpace(spaceSum?.roomId, uSession.sessionId) uiStateRepository.storeSelectedSpace(spaceSummary?.roomId, activeSession.sessionId)
} }
if (spaceSum == null) { if (spaceSummary == null) {
selectedSpaceDataSource.post(Option.empty()) selectedSpaceDataSource.post(Option.empty())
} else { } else {
selectedSpaceDataSource.post(Option.just(spaceSum)) selectedSpaceDataSource.post(Option.just(spaceSummary))
} }
if (spaceId != null) { if (spaceId != null) {
uSession.coroutineScope.launch(Dispatchers.IO) { activeSession.coroutineScope.launch(Dispatchers.IO) {
tryOrNull { tryOrNull {
uSession.getRoom(spaceId)?.membershipService()?.loadRoomMembersIfNeeded() activeSession.getRoom(spaceId)?.membershipService()?.loadRoomMembersIfNeeded()
} }
} }
} }
@ -122,9 +127,11 @@ class AppStateHandler @Inject constructor(
}.launchIn(session.coroutineScope) }.launchIn(session.coroutineScope)
} }
fun getSpaceBackstack() = spaceBackstack override fun getSpaceBackstack() = spaceBackstack
fun safeActiveSpaceId(): String? { override fun getSelectedSpaceFlow() = selectedSpaceFlow
override fun getSafeActiveSpaceId(): String? {
return selectedSpaceDataSource.currentValue?.orNull()?.roomId return selectedSpaceDataSource.currentValue?.orNull()?.roomId
} }

View File

@ -88,7 +88,7 @@ class VectorApplication :
@Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var versionProvider: VersionProvider @Inject lateinit var versionProvider: VersionProvider
@Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var appStateHandler: AppStateHandler @Inject lateinit var spaceStateHandler: SpaceStateHandler
@Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var popupAlertManager: PopupAlertManager
@Inject lateinit var pinLocker: PinLocker @Inject lateinit var pinLocker: PinLocker
@Inject lateinit var callManager: WebRtcCallManager @Inject lateinit var callManager: WebRtcCallManager
@ -177,7 +177,7 @@ class VectorApplication :
fcmHelper.onEnterBackground(activeSessionHolder) fcmHelper.onEnterBackground(activeSessionHolder)
} }
}) })
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler) ProcessLifecycleOwner.get().lifecycle.addObserver(spaceStateHandler)
ProcessLifecycleOwner.get().lifecycle.addObserver(pinLocker) ProcessLifecycleOwner.get().lifecycle.addObserver(pinLocker)
ProcessLifecycleOwner.get().lifecycle.addObserver(callManager) ProcessLifecycleOwner.get().lifecycle.addObserver(callManager)
// This should be done as early as possible // This should be done as early as possible

View File

@ -31,6 +31,8 @@ import dagger.hilt.components.SingletonComponent
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
import im.vector.app.EmojiCompatWrapper import im.vector.app.EmojiCompatWrapper
import im.vector.app.EmojiSpanify import im.vector.app.EmojiSpanify
import im.vector.app.SpaceStateHandler
import im.vector.app.SpaceStateHandlerImpl
import im.vector.app.config.analyticsConfig import im.vector.app.config.analyticsConfig
import im.vector.app.core.dispatchers.CoroutineDispatchers import im.vector.app.core.dispatchers.CoroutineDispatchers
import im.vector.app.core.error.DefaultErrorFormatter import im.vector.app.core.error.DefaultErrorFormatter
@ -108,6 +110,9 @@ abstract class VectorBindModule {
@Binds @Binds
abstract fun bindSystemSettingsProvide(provider: AndroidSystemSettingsProvider): SystemSettingsProvider abstract fun bindSystemSettingsProvide(provider: AndroidSystemSettingsProvider): SystemSettingsProvider
@Binds
abstract fun bindSpaceStateHandler(spaceStateHandlerImpl: SpaceStateHandlerImpl): SpaceStateHandler
} }
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)

View File

@ -35,8 +35,8 @@ import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
@ -130,7 +130,7 @@ class HomeActivity :
@Inject lateinit var permalinkHandler: PermalinkHandler @Inject lateinit var permalinkHandler: PermalinkHandler
@Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var avatarRenderer: AvatarRenderer
@Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter @Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter
@Inject lateinit var appStateHandler: AppStateHandler @Inject lateinit var spaceStateHandler: SpaceStateHandler
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper @Inject lateinit var unifiedPushHelper: UnifiedPushHelper
@Inject lateinit var fcmHelper: FcmHelper @Inject lateinit var fcmHelper: FcmHelper
@Inject lateinit var nightlyProxy: NightlyProxy @Inject lateinit var nightlyProxy: NightlyProxy

View File

@ -28,8 +28,8 @@ import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeDrawable
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.SpaceStateHandler
import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.OnBackPressed
@ -68,7 +68,7 @@ class HomeDetailFragment @Inject constructor(
private val alertManager: PopupAlertManager, private val alertManager: PopupAlertManager,
private val callManager: WebRtcCallManager, private val callManager: WebRtcCallManager,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val appStateHandler: AppStateHandler, private val spaceStateHandler: SpaceStateHandler,
private val vectorFeatures: VectorFeatures, private val vectorFeatures: VectorFeatures,
) : VectorBaseFragment<FragmentHomeDetailBinding>(), ) : VectorBaseFragment<FragmentHomeDetailBinding>(),
KeysBackupBanner.Delegate, KeysBackupBanner.Delegate,
@ -186,13 +186,13 @@ class HomeDetailFragment @Inject constructor(
} }
private fun navigateBack() { private fun navigateBack() {
val previousSpaceId = appStateHandler.getSpaceBackstack().removeLastOrNull() val previousSpaceId = spaceStateHandler.getSpaceBackstack().removeLastOrNull()
val parentSpaceId = appStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull() val parentSpaceId = spaceStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
setCurrentSpace(previousSpaceId ?: parentSpaceId) setCurrentSpace(previousSpaceId ?: parentSpaceId)
} }
private fun setCurrentSpace(spaceId: String?) { private fun setCurrentSpace(spaceId: String?) {
appStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false) spaceStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
sharedActionViewModel.post(HomeActivitySharedAction.OnCloseSpace) sharedActionViewModel.post(HomeActivitySharedAction.OnCloseSpace)
} }
@ -215,7 +215,7 @@ class HomeDetailFragment @Inject constructor(
} }
private fun refreshSpaceState() { private fun refreshSpaceState() {
appStateHandler.getCurrentSpace()?.let { spaceStateHandler.getCurrentSpace()?.let {
onSpaceChange(it) onSpaceChange(it)
} }
} }
@ -473,7 +473,7 @@ class HomeDetailFragment @Inject constructor(
return this return this
} }
override fun onBackPressed(toolbarButton: Boolean) = if (appStateHandler.getCurrentSpace() != null) { override fun onBackPressed(toolbarButton: Boolean) = if (spaceStateHandler.getCurrentSpace() != null) {
navigateBack() navigateBack()
true true
} else { } else {

View File

@ -22,7 +22,7 @@ import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.singletonEntryPoint
@ -68,7 +68,7 @@ class HomeDetailViewModel @AssistedInject constructor(
private val vectorDataStore: VectorDataStore, private val vectorDataStore: VectorDataStore,
private val callManager: WebRtcCallManager, private val callManager: WebRtcCallManager,
private val directRoomHelper: DirectRoomHelper, private val directRoomHelper: DirectRoomHelper,
private val appStateHandler: AppStateHandler, private val spaceStateHandler: SpaceStateHandler,
private val autoAcceptInvites: AutoAcceptInvites, private val autoAcceptInvites: AutoAcceptInvites,
private val vectorOverrides: VectorOverrides private val vectorOverrides: VectorOverrides
) : VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState), ) : VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState),
@ -206,7 +206,7 @@ class HomeDetailViewModel @AssistedInject constructor(
} }
private fun observeRoomGroupingMethod() { private fun observeRoomGroupingMethod() {
appStateHandler.selectedSpaceFlow spaceStateHandler.getSelectedSpaceFlow()
.setOnEach { .setOnEach {
copy( copy(
selectedSpace = it.orNull() selectedSpace = it.orNull()
@ -215,7 +215,7 @@ class HomeDetailViewModel @AssistedInject constructor(
} }
private fun observeRoomSummaries() { private fun observeRoomSummaries() {
appStateHandler.selectedSpaceFlow.distinctUntilChanged().flatMapLatest { spaceStateHandler.getSelectedSpaceFlow().distinctUntilChanged().flatMapLatest {
// we use it as a trigger to all changes in room, but do not really load // we use it as a trigger to all changes in room, but do not really load
// the actual models // the actual models
session.roomService().getPagedRoomSummariesLive( session.roomService().getPagedRoomSummariesLive(
@ -227,7 +227,7 @@ class HomeDetailViewModel @AssistedInject constructor(
} }
.throttleFirst(300) .throttleFirst(300)
.onEach { .onEach {
val activeSpaceRoomId = appStateHandler.getCurrentSpace()?.roomId val activeSpaceRoomId = spaceStateHandler.getCurrentSpace()?.roomId
var dmInvites = 0 var dmInvites = 0
var roomsInvite = 0 var roomsInvite = 0
if (autoAcceptInvites.showInvites()) { if (autoAcceptInvites.showInvites()) {

View File

@ -22,7 +22,7 @@ import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyAction
@ -58,7 +58,7 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(
@Assisted initialState: UnreadMessagesState, @Assisted initialState: UnreadMessagesState,
session: Session, session: Session,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
appStateHandler: AppStateHandler, spaceStateHandler: SpaceStateHandler,
private val autoAcceptInvites: AutoAcceptInvites private val autoAcceptInvites: AutoAcceptInvites
) : ) :
VectorViewModel<UnreadMessagesState, EmptyAction, EmptyViewEvents>(initialState) { VectorViewModel<UnreadMessagesState, EmptyAction, EmptyViewEvents>(initialState) {
@ -109,8 +109,8 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(
} }
combine( combine(
appStateHandler.selectedSpaceFlow.distinctUntilChanged(), spaceStateHandler.getSelectedSpaceFlow().distinctUntilChanged(),
appStateHandler.selectedSpaceFlow.flatMapLatest { spaceStateHandler.getSelectedSpaceFlow().flatMapLatest {
roomService.getPagedRoomSummariesLive( roomService.getPagedRoomSummariesLive(
roomSummaryQueryParams { roomSummaryQueryParams {
this.memberships = Membership.activeMemberships() this.memberships = Membership.activeMemberships()
@ -162,10 +162,10 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(
CountInfo( CountInfo(
homeCount = counts, homeCount = counts,
otherCount = RoomAggregateNotificationCount( otherCount = RoomAggregateNotificationCount(
notificationCount = rootCounts.fold(0, { acc, rs -> acc + rs.notificationCount }) + notificationCount = rootCounts.fold(0) { acc, rs -> acc + rs.notificationCount } +
(counts.notificationCount.takeIf { selectedSpace != null } ?: 0) + (counts.notificationCount.takeIf { selectedSpace != null } ?: 0) +
spaceInviteCount, spaceInviteCount,
highlightCount = rootCounts.fold(0, { acc, rs -> acc + rs.highlightCount }) + highlightCount = rootCounts.fold(0) { acc, rs -> acc + rs.highlightCount } +
(counts.highlightCount.takeIf { selectedSpace != null } ?: 0) + (counts.highlightCount.takeIf { selectedSpace != null } ?: 0) +
spaceInviteCount spaceInviteCount
) )

View File

@ -27,8 +27,8 @@ import com.airbnb.mvrx.Uninitialized
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.mvrx.runCatchingToAsync
@ -138,7 +138,7 @@ class TimelineViewModel @AssistedInject constructor(
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase, private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
private val redactLiveLocationShareEventUseCase: RedactLiveLocationShareEventUseCase, private val redactLiveLocationShareEventUseCase: RedactLiveLocationShareEventUseCase,
timelineFactory: TimelineFactory, timelineFactory: TimelineFactory,
appStateHandler: AppStateHandler, spaceStateHandler: SpaceStateHandler,
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), ) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback { Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback {
@ -220,16 +220,16 @@ class TimelineViewModel @AssistedInject constructor(
if (initialState.switchToParentSpace) { if (initialState.switchToParentSpace) {
// We are coming from a notification, try to switch to the most relevant space // We are coming from a notification, try to switch to the most relevant space
// so that when hitting back the room will appear in the list // so that when hitting back the room will appear in the list
appStateHandler.getCurrentSpace().let { currentSpace -> spaceStateHandler.getCurrentSpace().let { currentSpace ->
val currentRoomSummary = room.roomSummary() ?: return@let val currentRoomSummary = room.roomSummary() ?: return@let
// nothing we are good // nothing we are good
if ((currentSpace == null && !vectorPreferences.prefSpacesShowAllRoomInHome()) || if ((currentSpace == null && !vectorPreferences.prefSpacesShowAllRoomInHome()) ||
(currentSpace != null && !currentRoomSummary.flattenParentIds.contains(currentSpace.roomId))) { (currentSpace != null && !currentRoomSummary.flattenParentIds.contains(currentSpace.roomId))) {
// take first one or switch to home // take first one or switch to home
appStateHandler.setCurrentSpace( spaceStateHandler.setCurrentSpace(
currentRoomSummary currentRoomSummary
.flattenParentIds.firstOrNull { it.isNotBlank() }, .flattenParentIds.firstOrNull { it.isNotBlank() },
// force persist, because if not on resume the AppStateHandler will resume // force persist, because if not on resume the SpaceStateHandler will resume
// the current space from what was persisted on enter background // the current space from what was persisted on enter background
persistNow = true persistNow = true
) )

View File

@ -23,8 +23,8 @@ import androidx.lifecycle.asFlow
import androidx.lifecycle.liveData import androidx.lifecycle.liveData
import androidx.paging.PagedList import androidx.paging.PagedList
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.SpaceStateHandler
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.features.invite.AutoAcceptInvites
@ -59,7 +59,7 @@ import timber.log.Timber
class RoomListSectionBuilder( class RoomListSectionBuilder(
private val session: Session, private val session: Session,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val appStateHandler: AppStateHandler, private val spaceStateHandler: SpaceStateHandler,
private val viewModelScope: CoroutineScope, private val viewModelScope: CoroutineScope,
private val autoAcceptInvites: AutoAcceptInvites, private val autoAcceptInvites: AutoAcceptInvites,
private val onUpdatable: (UpdatableLivePageResult) -> Unit, private val onUpdatable: (UpdatableLivePageResult) -> Unit,
@ -95,7 +95,7 @@ class RoomListSectionBuilder(
} }
} }
appStateHandler.selectedSpaceFlow spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged() .distinctUntilChanged()
.onEach { selectedSpaceOption -> .onEach { selectedSpaceOption ->
val selectedSpace = selectedSpaceOption.orNull() val selectedSpace = selectedSpaceOption.orNull()
@ -150,7 +150,7 @@ class RoomListSectionBuilder(
) { ) {
it.memberships = listOf(Membership.JOIN) it.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
it.roomTagQueryFilter = RoomTagQueryFilter(false, false, false) it.roomTagQueryFilter = RoomTagQueryFilter(isFavorite = false, isLowPriority = false, isServerNotice = false)
} }
addSection( addSection(
@ -187,7 +187,7 @@ class RoomListSectionBuilder(
// add suggested rooms // add suggested rooms
val suggestedRoomsFlow = // MutableLiveData<List<SpaceChildInfo>>() val suggestedRoomsFlow = // MutableLiveData<List<SpaceChildInfo>>()
appStateHandler.selectedSpaceFlow spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged() .distinctUntilChanged()
.flatMapLatest { selectedSpaceOption -> .flatMapLatest { selectedSpaceOption ->
val selectedSpace = selectedSpaceOption.orNull() val selectedSpace = selectedSpaceOption.orNull()
@ -271,7 +271,7 @@ class RoomListSectionBuilder(
) { ) {
it.memberships = listOf(Membership.JOIN) it.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
it.roomTagQueryFilter = RoomTagQueryFilter(false, false, null) it.roomTagQueryFilter = RoomTagQueryFilter(isFavorite = false, isLowPriority = false, isServerNotice = null)
} }
addSection( addSection(
@ -283,7 +283,7 @@ class RoomListSectionBuilder(
) { ) {
it.memberships = listOf(Membership.JOIN) it.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
it.roomTagQueryFilter = RoomTagQueryFilter(false, true, null) it.roomTagQueryFilter = RoomTagQueryFilter(isFavorite = false, isLowPriority = true, isServerNotice = null)
} }
} }
@ -360,7 +360,7 @@ class RoomListSectionBuilder(
query: (RoomSummaryQueryParams.Builder) -> Unit query: (RoomSummaryQueryParams.Builder) -> Unit
) { ) {
withQueryParams(query) { roomQueryParams -> withQueryParams(query) { roomQueryParams ->
val updatedQueryParams = roomQueryParams.process(spaceFilterStrategy, appStateHandler.safeActiveSpaceId()) val updatedQueryParams = roomQueryParams.process(spaceFilterStrategy, spaceStateHandler.getSafeActiveSpaceId())
val liveQueryParams = MutableStateFlow(updatedQueryParams) val liveQueryParams = MutableStateFlow(updatedQueryParams)
val itemCountFlow = liveQueryParams val itemCountFlow = liveQueryParams
.flatMapLatest { .flatMapLatest {
@ -371,7 +371,7 @@ class RoomListSectionBuilder(
val name = stringProvider.getString(nameRes) val name = stringProvider.getString(nameRes)
val filteredPagedRoomSummariesLive = session.roomService().getFilteredPagedRoomSummariesLive( val filteredPagedRoomSummariesLive = session.roomService().getFilteredPagedRoomSummariesLive(
roomQueryParams.process(spaceFilterStrategy, appStateHandler.safeActiveSpaceId()), roomQueryParams.process(spaceFilterStrategy, spaceStateHandler.getSafeActiveSpaceId()),
pagedListConfig pagedListConfig
) )
when (spaceFilterStrategy) { when (spaceFilterStrategy) {
@ -418,7 +418,7 @@ class RoomListSectionBuilder(
RoomAggregateNotificationCount(it.size, it.size) RoomAggregateNotificationCount(it.size, it.size)
} else { } else {
session.roomService().getNotificationCountForRooms( session.roomService().getNotificationCountForRooms(
roomQueryParams.process(spaceFilterStrategy, appStateHandler.safeActiveSpaceId()) roomQueryParams.process(spaceFilterStrategy, spaceStateHandler.getSafeActiveSpaceId())
) )
} }
) )

View File

@ -25,7 +25,7 @@ import com.airbnb.mvrx.Success
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
@ -60,7 +60,7 @@ class RoomListViewModel @AssistedInject constructor(
@Assisted initialState: RoomListViewState, @Assisted initialState: RoomListViewState,
private val session: Session, private val session: Session,
stringProvider: StringProvider, stringProvider: StringProvider,
appStateHandler: AppStateHandler, spaceStateHandler: SpaceStateHandler,
vectorPreferences: VectorPreferences, vectorPreferences: VectorPreferences,
autoAcceptInvites: AutoAcceptInvites, autoAcceptInvites: AutoAcceptInvites,
private val analyticsTracker: AnalyticsTracker private val analyticsTracker: AnalyticsTracker
@ -100,7 +100,7 @@ class RoomListViewModel @AssistedInject constructor(
observeMembershipChanges() observeMembershipChanges()
observeLocalRooms() observeLocalRooms()
appStateHandler.selectedSpaceFlow spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged() .distinctUntilChanged()
.execute { .execute {
copy( copy(
@ -146,17 +146,17 @@ class RoomListViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by hiltMavericksViewModelFactory() companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by hiltMavericksViewModelFactory()
private val roomListSectionBuilder = RoomListSectionBuilder( private val roomListSectionBuilder = RoomListSectionBuilder(
session, session,
stringProvider, stringProvider,
appStateHandler, spaceStateHandler,
viewModelScope, viewModelScope,
autoAcceptInvites, autoAcceptInvites,
{ {
updatableQuery = it updatableQuery = it
}, },
suggestedRoomJoiningState, suggestedRoomJoiningState,
!vectorPreferences.prefSpacesShowAllRoomInHome() !vectorPreferences.prefSpacesShowAllRoomInHome()
) )
val sections: List<RoomsSection> by lazy { val sections: List<RoomsSection> by lazy {
roomListSectionBuilder.buildSections(initialState.displayMode) roomListSectionBuilder.buildSections(initialState.displayMode)

View File

@ -22,7 +22,7 @@ import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.StateView import im.vector.app.core.platform.StateView
@ -50,7 +50,7 @@ import org.matrix.android.sdk.api.session.room.state.isPublic
class HomeRoomListViewModel @AssistedInject constructor( class HomeRoomListViewModel @AssistedInject constructor(
@Assisted initialState: HomeRoomListViewState, @Assisted initialState: HomeRoomListViewState,
private val session: Session, private val session: Session,
private val appStateHandler: AppStateHandler, private val spaceStateHandler: SpaceStateHandler,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
) : VectorViewModel<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) { ) : VectorViewModel<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) {
@ -99,10 +99,10 @@ class HomeRoomListViewModel @AssistedInject constructor(
pagedListConfig pagedListConfig
) )
appStateHandler.selectedSpaceFlow spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged() .distinctUntilChanged()
.onStart { .onStart {
emit(appStateHandler.getCurrentSpace().toOption()) emit(spaceStateHandler.getCurrentSpace().toOption())
} }
.onEach { selectedSpaceOption -> .onEach { selectedSpaceOption ->
val selectedSpace = selectedSpaceOption.orNull() val selectedSpace = selectedSpaceOption.orNull()

View File

@ -31,8 +31,8 @@ import androidx.core.app.TaskStackBuilder
import androidx.core.util.Pair import androidx.core.util.Pair
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.fatalError import im.vector.app.core.error.fatalError
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
@ -120,7 +120,7 @@ class DefaultNavigator @Inject constructor(
private val sessionHolder: ActiveSessionHolder, private val sessionHolder: ActiveSessionHolder,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val widgetArgsBuilder: WidgetArgsBuilder, private val widgetArgsBuilder: WidgetArgsBuilder,
private val appStateHandler: AppStateHandler, private val spaceStateHandler: SpaceStateHandler,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider,
private val features: VectorFeatures, private val features: VectorFeatures,
private val analyticsTracker: AnalyticsTracker private val analyticsTracker: AnalyticsTracker
@ -167,7 +167,7 @@ class DefaultNavigator @Inject constructor(
analyticsTracker.capture( analyticsTracker.capture(
sessionHolder.getActiveSession().getRoomSummary(roomId).toAnalyticsViewRoom( sessionHolder.getActiveSession().getRoomSummary(roomId).toAnalyticsViewRoom(
trigger = trigger, trigger = trigger,
selectedSpace = appStateHandler.getCurrentSpace() selectedSpace = spaceStateHandler.getCurrentSpace()
) )
) )
} }
@ -182,7 +182,7 @@ class DefaultNavigator @Inject constructor(
fatalError("Trying to open an unknown space $spaceId", vectorPreferences.failFast()) fatalError("Trying to open an unknown space $spaceId", vectorPreferences.failFast())
return return
} }
appStateHandler.setCurrentSpace(spaceId) spaceStateHandler.setCurrentSpace(spaceId)
when (postSwitchSpaceAction) { when (postSwitchSpaceAction) {
Navigator.PostSwitchSpaceAction.None -> { Navigator.PostSwitchSpaceAction.None -> {
// go back to home if we are showing room details? // go back to home if we are showing room details?
@ -318,7 +318,7 @@ class DefaultNavigator @Inject constructor(
} }
override fun openRoomDirectory(context: Context, initialFilter: String) { override fun openRoomDirectory(context: Context, initialFilter: String) {
when (val currentSpace = appStateHandler.getCurrentSpace()) { when (val currentSpace = spaceStateHandler.getCurrentSpace()) {
null -> RoomDirectoryActivity.getIntent(context, initialFilter) null -> RoomDirectoryActivity.getIntent(context, initialFilter)
else -> SpaceExploreActivity.newIntent(context, currentSpace.roomId) else -> SpaceExploreActivity.newIntent(context, currentSpace.roomId)
}.start(context) }.start(context)
@ -330,14 +330,14 @@ class DefaultNavigator @Inject constructor(
} }
override fun openCreateDirectRoom(context: Context) { override fun openCreateDirectRoom(context: Context) {
when (val currentSpace = appStateHandler.getCurrentSpace()) { when (val currentSpace = spaceStateHandler.getCurrentSpace()) {
null -> CreateDirectRoomActivity.getIntent(context) null -> CreateDirectRoomActivity.getIntent(context)
else -> SpacePeopleActivity.newIntent(context, currentSpace.roomId) else -> SpacePeopleActivity.newIntent(context, currentSpace.roomId)
}.start(context) }.start(context)
} }
override fun openInviteUsersToRoom(context: Context, roomId: String) { override fun openInviteUsersToRoom(context: Context, roomId: String) {
when (val currentSpace = appStateHandler.getCurrentSpace()) { when (val currentSpace = spaceStateHandler.getCurrentSpace()) {
null -> InviteUsersToRoomActivity.getIntent(context, roomId).start(context) null -> InviteUsersToRoomActivity.getIntent(context, roomId).start(context)
else -> showInviteToDialog(context, currentSpace, roomId) else -> showInviteToDialog(context, currentSpace, roomId)
} }

View File

@ -25,7 +25,7 @@ import com.airbnb.mvrx.Uninitialized
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
@ -58,7 +58,7 @@ class CreateRoomViewModel @AssistedInject constructor(
@Assisted private val initialState: CreateRoomViewState, @Assisted private val initialState: CreateRoomViewState,
private val session: Session, private val session: Session,
private val rawService: RawService, private val rawService: RawService,
appStateHandler: AppStateHandler, spaceStateHandler: SpaceStateHandler,
private val analyticsTracker: AnalyticsTracker private val analyticsTracker: AnalyticsTracker
) : VectorViewModel<CreateRoomViewState, CreateRoomAction, CreateRoomViewEvents>(initialState) { ) : VectorViewModel<CreateRoomViewState, CreateRoomAction, CreateRoomViewEvents>(initialState) {
@ -73,7 +73,7 @@ class CreateRoomViewModel @AssistedInject constructor(
initHomeServerName() initHomeServerName()
initAdminE2eByDefault() initAdminE2eByDefault()
val parentSpaceId = initialState.parentSpaceId ?: appStateHandler.safeActiveSpaceId() val parentSpaceId = initialState.parentSpaceId ?: spaceStateHandler.getSafeActiveSpaceId()
val restrictedSupport = session.homeServerCapabilitiesService().getHomeServerCapabilities() val restrictedSupport = session.homeServerCapabilitiesService().getHomeServerCapabilities()
.isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED) .isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED)

View File

@ -23,7 +23,7 @@ import com.airbnb.mvrx.Success
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
@ -61,7 +61,7 @@ import org.matrix.android.sdk.flow.flow
class SpaceListViewModel @AssistedInject constructor( class SpaceListViewModel @AssistedInject constructor(
@Assisted initialState: SpaceListViewState, @Assisted initialState: SpaceListViewState,
private val appStateHandler: AppStateHandler, private val spaceStateHandler: SpaceStateHandler,
private val session: Session, private val session: Session,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val autoAcceptInvites: AutoAcceptInvites, private val autoAcceptInvites: AutoAcceptInvites,
@ -85,8 +85,7 @@ class SpaceListViewModel @AssistedInject constructor(
} }
observeSpaceSummaries() observeSpaceSummaries()
// observeSelectionState() spaceStateHandler.getSelectedSpaceFlow()
appStateHandler.selectedSpaceFlow
.distinctUntilChanged() .distinctUntilChanged()
.setOnEach { selectedSpaceOption -> .setOnEach { selectedSpaceOption ->
copy( copy(
@ -219,7 +218,7 @@ class SpaceListViewModel @AssistedInject constructor(
if (state.selectedSpace?.roomId != action.spaceSummary?.roomId) { if (state.selectedSpace?.roomId != action.spaceSummary?.roomId) {
analyticsTracker.capture(Interaction(null, null, Interaction.Name.SpacePanelSwitchSpace)) analyticsTracker.capture(Interaction(null, null, Interaction.Name.SpacePanelSwitchSpace))
setState { copy(selectedSpace = action.spaceSummary) } setState { copy(selectedSpace = action.spaceSummary) }
appStateHandler.setCurrentSpace(action.spaceSummary?.roomId) spaceStateHandler.setCurrentSpace(action.spaceSummary?.roomId)
_viewEvents.post(SpaceListViewEvents.CloseDrawer) _viewEvents.post(SpaceListViewEvents.CloseDrawer)
} else { } else {
analyticsTracker.capture(Interaction(null, null, Interaction.Name.SpacePanelSelectedSpace)) analyticsTracker.capture(Interaction(null, null, Interaction.Name.SpacePanelSelectedSpace))

View File

@ -24,7 +24,7 @@ import com.airbnb.mvrx.Uninitialized
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.EmptyViewEvents
@ -50,7 +50,7 @@ import timber.log.Timber
class SpaceMenuViewModel @AssistedInject constructor( class SpaceMenuViewModel @AssistedInject constructor(
@Assisted val initialState: SpaceMenuState, @Assisted val initialState: SpaceMenuState,
val session: Session, val session: Session,
val appStateHandler: AppStateHandler val spaceStateHandler: SpaceStateHandler
) : VectorViewModel<SpaceMenuState, SpaceLeaveViewAction, EmptyViewEvents>(initialState) { ) : VectorViewModel<SpaceMenuState, SpaceLeaveViewAction, EmptyViewEvents>(initialState) {
@AssistedFactory @AssistedFactory
@ -73,9 +73,9 @@ class SpaceMenuViewModel @AssistedInject constructor(
it.getOrNull()?.let { it.getOrNull()?.let {
if (it.membership == Membership.LEAVE) { if (it.membership == Membership.LEAVE) {
setState { copy(leavingState = Success(Unit)) } setState { copy(leavingState = Success(Unit)) }
if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { if (spaceStateHandler.getSafeActiveSpaceId() == initialState.spaceId) {
// switch to home? // switch to home?
appStateHandler.setCurrentSpace(null, session) spaceStateHandler.setCurrentSpace(null, session)
} }
} }
} }

View File

@ -24,7 +24,7 @@ import com.airbnb.mvrx.Uninitialized
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.EmptyViewEvents
@ -53,7 +53,7 @@ import timber.log.Timber
class SpaceLeaveAdvancedViewModel @AssistedInject constructor( class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
@Assisted val initialState: SpaceLeaveAdvanceViewState, @Assisted val initialState: SpaceLeaveAdvanceViewState,
private val session: Session, private val session: Session,
private val appStateHandler: AppStateHandler private val spaceStateHandler: SpaceStateHandler
) : VectorViewModel<SpaceLeaveAdvanceViewState, SpaceLeaveAdvanceViewAction, EmptyViewEvents>(initialState) { ) : VectorViewModel<SpaceLeaveAdvanceViewState, SpaceLeaveAdvanceViewAction, EmptyViewEvents>(initialState) {
init { init {
@ -75,19 +75,19 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
} }
setState { copy(spaceSummary = spaceSummary) } setState { copy(spaceSummary = spaceSummary) }
session.getRoom(initialState.spaceId)?.let { room -> session.getRoom(initialState.spaceId)
room.flow().liveRoomSummary() ?.flow()
.unwrap() ?.liveRoomSummary()
.onEach { ?.unwrap()
if (it.membership == Membership.LEAVE) { ?.onEach {
setState { copy(leaveState = Success(Unit)) } if (it.membership == Membership.LEAVE) {
if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { setState { copy(leaveState = Success(Unit)) }
// switch to home? if (spaceStateHandler.getSafeActiveSpaceId() == initialState.spaceId) {
appStateHandler.setCurrentSpace(null, session) // switch to home?
} spaceStateHandler.setCurrentSpace(null, session)
} }
}.launchIn(viewModelScope) }
} }?.launchIn(viewModelScope)
viewModelScope.launch { viewModelScope.launch {
val children = session.roomService().getRoomSummaries( val children = session.roomService().getRoomSummaries(

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2022 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
import im.vector.app.test.fakes.FakeActiveSessionDataSource
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeAnalyticsTracker
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fakes.FakeUiStateRepository
import im.vector.app.test.fixtures.RoomSummaryFixture.aRoomSummary
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBe
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
internal class SpaceStateHandlerImplTest {
private val spaceId = "spaceId"
private val spaceSummary = aRoomSummary(spaceId)
private val session = FakeSession.withRoomSummary(spaceSummary)
private val sessionDataSource = FakeActiveSessionDataSource()
private val uiStateRepository = FakeUiStateRepository()
private val activeSessionHolder = FakeActiveSessionHolder(session)
private val analyticsTracker = FakeAnalyticsTracker()
private val spaceStateHandler = SpaceStateHandlerImpl(
sessionDataSource.instance,
uiStateRepository,
activeSessionHolder.instance,
analyticsTracker,
)
@Test
fun `given selected space doesn't exist, when getCurrentSpace, then return null`() {
val currentSpace = spaceStateHandler.getCurrentSpace()
currentSpace shouldBe null
}
@Test
fun `given selected space exists, when getCurrentSpace, then return selected space`() {
spaceStateHandler.setCurrentSpace(spaceId, session)
val currentSpace = spaceStateHandler.getCurrentSpace()
currentSpace shouldBe spaceSummary
}
@Test
fun `given persistNow is true, when setCurrentSpace, then immediately persist to ui state`() {
spaceStateHandler.setCurrentSpace(spaceId, session, persistNow = true)
uiStateRepository.verifyStoreSelectedSpace(spaceId, session)
}
@Test
fun `given persistNow is false, when setCurrentSpace, then don't immediately persist to ui state`() {
spaceStateHandler.setCurrentSpace(spaceId, session, persistNow = false)
uiStateRepository.verifyStoreSelectedSpace(spaceId, session, inverse = true)
}
@Test
fun `given is forward navigation and no current space, when setCurrentSpace, then null added to backstack`() {
spaceStateHandler.setCurrentSpace(spaceId, session, isForwardNavigation = true)
val backstack = spaceStateHandler.getSpaceBackstack()
backstack.size shouldBe 1
backstack.first() shouldBe null
}
@Test
fun `given is forward navigation and is in space, when setCurrentSpace, then previous space added to backstack`() {
spaceStateHandler.setCurrentSpace(spaceId, session, isForwardNavigation = true)
spaceStateHandler.setCurrentSpace("secondSpaceId", session, isForwardNavigation = true)
val backstack = spaceStateHandler.getSpaceBackstack()
backstack.size shouldBe 2
backstack shouldBeEqualTo listOf(null, spaceId)
}
@Test
fun `given is not forward navigation, when setCurrentSpace, then previous space not added to backstack`() {
spaceStateHandler.setCurrentSpace(spaceId, session, isForwardNavigation = false)
val backstack = spaceStateHandler.getSpaceBackstack()
backstack.size shouldBe 0
}
@Test
fun `when setCurrentSpace, then space is emitted to selectedSpaceFlow`() = runTest {
spaceStateHandler.setCurrentSpace(spaceId, session)
val currentSpace = spaceStateHandler.getSelectedSpaceFlow().first().orNull()
currentSpace shouldBeEqualTo spaceSummary
}
@Test
fun `given current space exists, when getSafeActiveSpaceId, then return current space id`() {
spaceStateHandler.setCurrentSpace(spaceId, session)
val activeSpaceId = spaceStateHandler.getSafeActiveSpaceId()
activeSpaceId shouldBeEqualTo spaceId
}
@Test
fun `given current space doesn't exist, when getSafeActiveSpaceId, then return current null`() {
val activeSpaceId = spaceStateHandler.getSafeActiveSpaceId()
activeSpaceId shouldBe null
}
}

View File

@ -23,11 +23,14 @@ import im.vector.app.features.session.VectorSessionStore
import im.vector.app.test.testCoroutineDispatchers import im.vector.app.test.testCoroutineDispatchers
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.coJustRun import io.mockk.coJustRun
import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic import io.mockk.mockkStatic
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.session.room.model.RoomSummary
class FakeSession( class FakeSession(
val fakeCryptoService: FakeCryptoService = FakeCryptoService(), val fakeCryptoService: FakeCryptoService = FakeCryptoService(),
@ -67,4 +70,11 @@ class FakeSession(
this@FakeSession.startSyncing(any()) this@FakeSession.startSyncing(any())
} }
} }
companion object {
fun withRoomSummary(roomSummary: RoomSummary) = FakeSession().apply {
every { getRoomSummary(any()) } returns roomSummary
}
}
} }

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 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.test.fakes
import im.vector.app.features.ui.UiStateRepository
import io.mockk.justRun
import io.mockk.mockk
import io.mockk.verify
import org.matrix.android.sdk.api.session.Session
class FakeUiStateRepository : UiStateRepository by mockk() {
init {
justRun { storeSelectedSpace(any(), any()) }
}
fun verifyStoreSelectedSpace(roomId: String, session: Session, inverse: Boolean = false) {
verify(inverse = inverse) { storeSelectedSpace(roomId, session.sessionId) }
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 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.test.fixtures
import org.matrix.android.sdk.api.session.room.model.RoomSummary
object RoomSummaryFixture {
fun aRoomSummary(roomId: String) = RoomSummary(
roomId,
isEncrypted = false,
encryptionEventTs = 0,
typingUsers = emptyList(),
)
}