Merge pull request #4958 from element-hq/feature/bma/splitPresenter
Split RoomListPresenter and introduce HomePresenter
This commit is contained in:
commit
c54608da44
60 changed files with 365 additions and 193 deletions
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
|
||||
sealed interface HomeEvents
|
||||
|
|
@ -44,7 +44,7 @@ import kotlinx.parcelize.Parcelize
|
|||
class HomeFlowNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: RoomListPresenter,
|
||||
private val presenter: HomePresenter,
|
||||
private val inviteFriendsUseCase: InviteFriendsUseCase,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val acceptDeclineInviteView: AcceptDeclineInviteView,
|
||||
|
|
@ -126,8 +126,8 @@ class HomeFlowNode @AssistedInject constructor(
|
|||
val state = presenter.present()
|
||||
val activity = requireNotNull(LocalActivity.current)
|
||||
|
||||
RoomListView(
|
||||
state = state,
|
||||
HomeView(
|
||||
homeState = state,
|
||||
onRoomClick = this::onRoomClick,
|
||||
onSettingsClick = this::onOpenSettings,
|
||||
onCreateRoomClick = this::onCreateRoomClick,
|
||||
|
|
@ -140,7 +140,7 @@ class HomeFlowNode @AssistedInject constructor(
|
|||
modifier = modifier,
|
||||
) {
|
||||
acceptDeclineInviteView.Render(
|
||||
state = state.acceptDeclineInviteState,
|
||||
state = state.roomListState.acceptDeclineInviteState,
|
||||
onAcceptInviteSuccess = this::onRoomClick,
|
||||
onDeclineInviteSuccess = { },
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||
import io.element.android.libraries.indicator.api.IndicatorService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomePresenter @Inject constructor(
|
||||
private val client: MatrixClient,
|
||||
private val syncService: SyncService,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
private val indicatorService: IndicatorService,
|
||||
private val roomListPresenter: Presenter<RoomListState>,
|
||||
private val logoutPresenter: Presenter<DirectLogoutState>,
|
||||
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
||||
) : Presenter<HomeState> {
|
||||
@Composable
|
||||
override fun present(): HomeState {
|
||||
val matrixUser = client.userProfile.collectAsState()
|
||||
val isOnline by syncService.isOnline.collectAsState()
|
||||
val canReportBug = remember { rageshakeFeatureAvailability.isAvailable() }
|
||||
val roomListState = roomListPresenter.present()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
// Force a refresh of the profile
|
||||
client.getUserProfile()
|
||||
}
|
||||
|
||||
// Avatar indicator
|
||||
val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator()
|
||||
|
||||
val directLogoutState = logoutPresenter.present()
|
||||
|
||||
fun handleEvents(event: HomeEvents) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||
|
||||
return HomeState(
|
||||
matrixUser = matrixUser.value,
|
||||
showAvatarIndicator = showAvatarIndicator,
|
||||
hasNetworkConnection = isOnline,
|
||||
roomListState = roomListState,
|
||||
snackbarMessage = snackbarMessage,
|
||||
canReportBug = canReportBug,
|
||||
directLogoutState = directLogoutState,
|
||||
eventSink = ::handleEvents,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
@Immutable
|
||||
data class HomeState(
|
||||
val matrixUser: MatrixUser,
|
||||
val showAvatarIndicator: Boolean,
|
||||
val hasNetworkConnection: Boolean,
|
||||
val roomListState: RoomListState,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val canReportBug: Boolean,
|
||||
val directLogoutState: DirectLogoutState,
|
||||
val eventSink: (HomeEvents) -> Unit,
|
||||
) {
|
||||
val displayActions = true
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.home.impl.roomlist.RoomListStateProvider
|
||||
import io.element.android.features.home.impl.roomlist.aRoomListState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
open class HomeStateProvider : PreviewParameterProvider<HomeState> {
|
||||
override val values: Sequence<HomeState>
|
||||
get() = sequenceOf(
|
||||
aHomeState(),
|
||||
aHomeState(hasNetworkConnection = false),
|
||||
aHomeState(snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete)),
|
||||
) + RoomListStateProvider().values.map {
|
||||
aHomeState(roomListState = it)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun aHomeState(
|
||||
matrixUser: MatrixUser = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"),
|
||||
showAvatarIndicator: Boolean = false,
|
||||
hasNetworkConnection: Boolean = true,
|
||||
snackbarMessage: SnackbarMessage? = null,
|
||||
roomListState: RoomListState = aRoomListState(),
|
||||
canReportBug: Boolean = true,
|
||||
directLogoutState: DirectLogoutState = aDirectLogoutState(),
|
||||
eventSink: (HomeEvents) -> Unit = {}
|
||||
) = HomeState(
|
||||
matrixUser = matrixUser,
|
||||
showAvatarIndicator = showAvatarIndicator,
|
||||
hasNetworkConnection = hasNetworkConnection,
|
||||
snackbarMessage = snackbarMessage,
|
||||
canReportBug = canReportBug,
|
||||
directLogoutState = directLogoutState,
|
||||
roomListState = roomListState,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
@ -29,6 +29,10 @@ import io.element.android.features.home.impl.components.RoomListContentView
|
|||
import io.element.android.features.home.impl.components.RoomListMenuAction
|
||||
import io.element.android.features.home.impl.components.RoomListTopBar
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.home.impl.roomlist.RoomListContextMenu
|
||||
import io.element.android.features.home.impl.roomlist.RoomListDeclineInviteMenu
|
||||
import io.element.android.features.home.impl.roomlist.RoomListEvents
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.home.impl.search.RoomListSearchView
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomView
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer
|
||||
|
|
@ -43,8 +47,8 @@ import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbar
|
|||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
@Composable
|
||||
fun RoomListView(
|
||||
state: RoomListState,
|
||||
fun HomeView(
|
||||
homeState: HomeState,
|
||||
onRoomClick: (RoomId) -> Unit,
|
||||
onSettingsClick: () -> Unit,
|
||||
onSetUpRecoveryClick: () -> Unit,
|
||||
|
|
@ -57,12 +61,13 @@ fun RoomListView(
|
|||
modifier: Modifier = Modifier,
|
||||
acceptDeclineInviteView: @Composable () -> Unit,
|
||||
) {
|
||||
val state: RoomListState = homeState.roomListState
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val firstThrottler = remember { FirstThrottler(300, coroutineScope) }
|
||||
|
||||
ConnectivityIndicatorContainer(
|
||||
modifier = modifier,
|
||||
isOnline = state.hasNetworkConnection,
|
||||
isOnline = homeState.hasNetworkConnection,
|
||||
) { topPadding ->
|
||||
Box {
|
||||
if (state.contextMenu is RoomListState.ContextMenu.Shown) {
|
||||
|
|
@ -85,8 +90,8 @@ fun RoomListView(
|
|||
|
||||
LeaveRoomView(state = state.leaveRoomState)
|
||||
|
||||
RoomListScaffold(
|
||||
state = state,
|
||||
HomeScaffold(
|
||||
state = homeState,
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = { if (firstThrottler.canHandle()) onRoomClick(it) },
|
||||
|
|
@ -114,8 +119,8 @@ fun RoomListView(
|
|||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun RoomListScaffold(
|
||||
state: RoomListState,
|
||||
private fun HomeScaffold(
|
||||
state: HomeState,
|
||||
onSetUpRecoveryClick: () -> Unit,
|
||||
onConfirmRecoveryKeyClick: () -> Unit,
|
||||
onRoomClick: (RoomId) -> Unit,
|
||||
|
|
@ -131,6 +136,7 @@ private fun RoomListScaffold(
|
|||
val appBarState = rememberTopAppBarState()
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
|
||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||
val roomListState: RoomListState = state.roomListState
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
|
|
@ -138,23 +144,23 @@ private fun RoomListScaffold(
|
|||
RoomListTopBar(
|
||||
matrixUser = state.matrixUser,
|
||||
showAvatarIndicator = state.showAvatarIndicator,
|
||||
areSearchResultsDisplayed = state.searchState.isSearchActive,
|
||||
onToggleSearch = { state.eventSink(RoomListEvents.ToggleSearchResults) },
|
||||
areSearchResultsDisplayed = roomListState.searchState.isSearchActive,
|
||||
onToggleSearch = { roomListState.eventSink(RoomListEvents.ToggleSearchResults) },
|
||||
onMenuActionClick = onMenuActionClick,
|
||||
onOpenSettings = onOpenSettings,
|
||||
scrollBehavior = scrollBehavior,
|
||||
displayMenuItems = state.displayActions,
|
||||
displayFilters = state.displayFilters,
|
||||
filtersState = state.filtersState,
|
||||
displayFilters = roomListState.displayFilters,
|
||||
filtersState = roomListState.filtersState,
|
||||
canReportBug = state.canReportBug,
|
||||
)
|
||||
},
|
||||
content = { padding ->
|
||||
RoomListContentView(
|
||||
contentState = state.contentState,
|
||||
filtersState = state.filtersState,
|
||||
hideInvitesAvatars = state.hideInvitesAvatars,
|
||||
eventSink = state.eventSink,
|
||||
contentState = roomListState.contentState,
|
||||
filtersState = roomListState.filtersState,
|
||||
hideInvitesAvatars = roomListState.hideInvitesAvatars,
|
||||
eventSink = roomListState.eventSink,
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = ::onRoomClick,
|
||||
|
|
@ -186,9 +192,9 @@ internal fun RoomListRoomSummary.contentType() = displayType.ordinal
|
|||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun RoomListViewPreview(@PreviewParameter(RoomListStateProvider::class) state: RoomListState) = ElementPreview {
|
||||
RoomListView(
|
||||
state = state,
|
||||
internal fun HomeViewPreview(@PreviewParameter(HomeStateProvider::class) state: HomeState) = ElementPreview {
|
||||
HomeView(
|
||||
homeState = state,
|
||||
onRoomClick = {},
|
||||
onSettingsClick = {},
|
||||
onSetUpRecoveryClick = {},
|
||||
|
|
@ -35,10 +35,6 @@ import androidx.compose.ui.unit.dp
|
|||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.home.impl.R
|
||||
import io.element.android.features.home.impl.RoomListContentState
|
||||
import io.element.android.features.home.impl.RoomListContentStateProvider
|
||||
import io.element.android.features.home.impl.RoomListEvents
|
||||
import io.element.android.features.home.impl.SecurityBannerState
|
||||
import io.element.android.features.home.impl.contentType
|
||||
import io.element.android.features.home.impl.filters.RoomListFilter
|
||||
import io.element.android.features.home.impl.filters.RoomListFiltersEmptyStateResources
|
||||
|
|
@ -47,6 +43,10 @@ import io.element.android.features.home.impl.filters.aRoomListFiltersState
|
|||
import io.element.android.features.home.impl.filters.selection.FilterSelectionState
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.home.impl.model.RoomSummaryDisplayType
|
||||
import io.element.android.features.home.impl.roomlist.RoomListContentState
|
||||
import io.element.android.features.home.impl.roomlist.RoomListContentStateProvider
|
||||
import io.element.android.features.home.impl.roomlist.RoomListEvents
|
||||
import io.element.android.features.home.impl.roomlist.SecurityBannerState
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
|
|
@ -311,7 +311,12 @@ internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStatePr
|
|||
RoomListContentView(
|
||||
contentState = state,
|
||||
filtersState = aRoomListFiltersState(
|
||||
filterSelectionStates = RoomListFilter.entries.map { FilterSelectionState(it, isSelected = true) }
|
||||
filterSelectionStates = RoomListFilter.entries.map {
|
||||
FilterSelectionState(
|
||||
filter = it,
|
||||
isSelected = true
|
||||
)
|
||||
}
|
||||
),
|
||||
hideInvitesAvatars = false,
|
||||
eventSink = {},
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ import androidx.compose.ui.unit.dp
|
|||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.home.impl.R
|
||||
import io.element.android.features.home.impl.RoomListEvents
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummaryProvider
|
||||
import io.element.android.features.home.impl.model.RoomSummaryDisplayType
|
||||
import io.element.android.features.home.impl.roomlist.RoomListEvents
|
||||
import io.element.android.libraries.core.extensions.orEmpty
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import dagger.Binds
|
|||
import dagger.Module
|
||||
import io.element.android.features.home.impl.filters.RoomListFiltersPresenter
|
||||
import io.element.android.features.home.impl.filters.RoomListFiltersState
|
||||
import io.element.android.features.home.impl.roomlist.RoomListPresenter
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.home.impl.search.RoomListSearchPresenter
|
||||
import io.element.android.features.home.impl.search.RoomListSearchState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
|
|
@ -20,6 +22,9 @@ import io.element.android.libraries.di.SessionScope
|
|||
@ContributesTo(SessionScope::class)
|
||||
@Module
|
||||
interface RoomListModule {
|
||||
@Binds
|
||||
fun bindRoomListPresenter(presenter: RoomListPresenter): Presenter<RoomListState>
|
||||
|
||||
@Binds
|
||||
fun bindSearchPresenter(presenter: RoomListSearchPresenter): Presenter<RoomListSearchState>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.text.font.FontStyle
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.home.impl.R
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
|
@ -20,6 +20,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.home.impl.R
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -33,22 +33,16 @@ import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteE
|
|||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent.ShowConfirmation
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||
import io.element.android.libraries.indicator.api.IndicatorService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
|
|
@ -75,15 +69,11 @@ import javax.inject.Inject
|
|||
private const val EXTENDED_RANGE_SIZE = 40
|
||||
private const val SUBSCRIBE_TO_VISIBLE_ROOMS_DEBOUNCE_IN_MILLIS = 300L
|
||||
|
||||
// TODO Create HomePresenter to split the state.
|
||||
class RoomListPresenter @Inject constructor(
|
||||
private val client: MatrixClient,
|
||||
private val syncService: SyncService,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
private val leaveRoomPresenter: Presenter<LeaveRoomState>,
|
||||
private val roomListDataSource: RoomListDataSource,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val indicatorService: IndicatorService,
|
||||
private val filtersPresenter: Presenter<RoomListFiltersState>,
|
||||
private val searchPresenter: Presenter<RoomListSearchState>,
|
||||
private val sessionPreferencesStore: SessionPreferencesStore,
|
||||
|
|
@ -92,9 +82,7 @@ class RoomListPresenter @Inject constructor(
|
|||
private val fullScreenIntentPermissionsPresenter: Presenter<FullScreenIntentPermissionsState>,
|
||||
private val batteryOptimizationPresenter: Presenter<BatteryOptimizationState>,
|
||||
private val notificationCleaner: NotificationCleaner,
|
||||
private val logoutPresenter: Presenter<DirectLogoutState>,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
||||
private val seenInvitesStore: SeenInvitesStore,
|
||||
) : Presenter<RoomListState> {
|
||||
private val encryptionService: EncryptionService = client.encryptionService()
|
||||
|
|
@ -103,23 +91,17 @@ class RoomListPresenter @Inject constructor(
|
|||
override fun present(): RoomListState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val leaveRoomState = leaveRoomPresenter.present()
|
||||
val matrixUser = client.userProfile.collectAsState()
|
||||
val isOnline by syncService.isOnline.collectAsState()
|
||||
val filtersState = filtersPresenter.present()
|
||||
val searchState = searchPresenter.present()
|
||||
val acceptDeclineInviteState = acceptDeclineInvitePresenter.present()
|
||||
val canReportBug = remember { rageshakeFeatureAvailability.isAvailable() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
roomListDataSource.launchIn(this)
|
||||
// Force a refresh of the profile
|
||||
client.getUserProfile()
|
||||
}
|
||||
|
||||
var securityBannerDismissed by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
// Avatar indicator
|
||||
val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator()
|
||||
val hideInvitesAvatar by remember {
|
||||
appPreferencesStore.getHideInviteAvatarsFlow()
|
||||
}.collectAsState(initial = false)
|
||||
|
|
@ -127,8 +109,6 @@ class RoomListPresenter @Inject constructor(
|
|||
val contextMenu = remember { mutableStateOf<RoomListState.ContextMenu>(RoomListState.ContextMenu.Hidden) }
|
||||
val declineInviteMenu = remember { mutableStateOf<RoomListState.DeclineInviteMenu>(RoomListState.DeclineInviteMenu.Hidden) }
|
||||
|
||||
val directLogoutState = logoutPresenter.present()
|
||||
|
||||
fun handleEvents(event: RoomListEvents) {
|
||||
when (event) {
|
||||
is RoomListEvents.UpdateVisibleRange -> coroutineScope.launch {
|
||||
|
|
@ -163,26 +143,18 @@ class RoomListPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||
|
||||
val contentState = roomListContentState(securityBannerDismissed)
|
||||
|
||||
val canReportRoom by produceState(false) { value = client.canReportRoom() }
|
||||
|
||||
return RoomListState(
|
||||
matrixUser = matrixUser.value,
|
||||
showAvatarIndicator = showAvatarIndicator,
|
||||
snackbarMessage = snackbarMessage,
|
||||
hasNetworkConnection = isOnline,
|
||||
contextMenu = contextMenu.value,
|
||||
declineInviteMenu = declineInviteMenu.value,
|
||||
leaveRoomState = leaveRoomState,
|
||||
filtersState = filtersState,
|
||||
canReportBug = canReportBug,
|
||||
searchState = searchState,
|
||||
contentState = contentState,
|
||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||
directLogoutState = directLogoutState,
|
||||
hideInvitesAvatars = hideInvitesAvatar,
|
||||
canReportRoom = canReportRoom,
|
||||
eventSink = ::handleEvents,
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.features.home.impl.filters.RoomListFiltersState
|
||||
|
|
@ -13,36 +13,26 @@ import io.element.android.features.home.impl.model.RoomListRoomSummary
|
|||
import io.element.android.features.home.impl.search.RoomListSearchState
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.push.api.battery.BatteryOptimizationState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
|
||||
@Immutable
|
||||
data class RoomListState(
|
||||
val matrixUser: MatrixUser,
|
||||
val showAvatarIndicator: Boolean,
|
||||
val hasNetworkConnection: Boolean,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val contextMenu: ContextMenu,
|
||||
val declineInviteMenu: DeclineInviteMenu,
|
||||
val leaveRoomState: LeaveRoomState,
|
||||
val filtersState: RoomListFiltersState,
|
||||
val canReportBug: Boolean,
|
||||
val searchState: RoomListSearchState,
|
||||
val contentState: RoomListContentState,
|
||||
val acceptDeclineInviteState: AcceptDeclineInviteState,
|
||||
val directLogoutState: DirectLogoutState,
|
||||
val hideInvitesAvatars: Boolean,
|
||||
val canReportRoom: Boolean,
|
||||
val eventSink: (RoomListEvents) -> Unit,
|
||||
) {
|
||||
val displayFilters = contentState is RoomListContentState.Rooms
|
||||
val displayActions = true
|
||||
|
||||
sealed interface ContextMenu {
|
||||
data object Hidden : ContextMenu
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.home.impl.filters.RoomListFiltersState
|
||||
|
|
@ -20,17 +20,11 @@ import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteE
|
|||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
import io.element.android.features.leaveroom.api.aLeaveRoomState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.push.api.battery.aBatteryOptimizationState
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -38,8 +32,6 @@ open class RoomListStateProvider : PreviewParameterProvider<RoomListState> {
|
|||
override val values: Sequence<RoomListState>
|
||||
get() = sequenceOf(
|
||||
aRoomListState(),
|
||||
aRoomListState(snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete)),
|
||||
aRoomListState(hasNetworkConnection = false),
|
||||
aRoomListState(contextMenu = aContextMenuShown(roomName = null)),
|
||||
aRoomListState(contextMenu = aContextMenuShown(roomName = "A nice room name")),
|
||||
aRoomListState(contextMenu = aContextMenuShown(isFavorite = true)),
|
||||
|
|
@ -53,36 +45,24 @@ open class RoomListStateProvider : PreviewParameterProvider<RoomListState> {
|
|||
}
|
||||
|
||||
internal fun aRoomListState(
|
||||
matrixUser: MatrixUser = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"),
|
||||
showAvatarIndicator: Boolean = false,
|
||||
hasNetworkConnection: Boolean = true,
|
||||
snackbarMessage: SnackbarMessage? = null,
|
||||
contextMenu: RoomListState.ContextMenu = RoomListState.ContextMenu.Hidden,
|
||||
declineInviteMenu: RoomListState.DeclineInviteMenu = RoomListState.DeclineInviteMenu.Hidden,
|
||||
leaveRoomState: LeaveRoomState = aLeaveRoomState(),
|
||||
searchState: RoomListSearchState = aRoomListSearchState(),
|
||||
filtersState: RoomListFiltersState = aRoomListFiltersState(),
|
||||
canReportBug: Boolean = true,
|
||||
contentState: RoomListContentState = aRoomsContentState(),
|
||||
acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(),
|
||||
directLogoutState: DirectLogoutState = aDirectLogoutState(),
|
||||
hideInvitesAvatars: Boolean = false,
|
||||
canReportRoom: Boolean = true,
|
||||
eventSink: (RoomListEvents) -> Unit = {}
|
||||
) = RoomListState(
|
||||
matrixUser = matrixUser,
|
||||
showAvatarIndicator = showAvatarIndicator,
|
||||
hasNetworkConnection = hasNetworkConnection,
|
||||
snackbarMessage = snackbarMessage,
|
||||
contextMenu = contextMenu,
|
||||
declineInviteMenu = declineInviteMenu,
|
||||
leaveRoomState = leaveRoomState,
|
||||
filtersState = filtersState,
|
||||
canReportBug = canReportBug,
|
||||
searchState = searchState,
|
||||
contentState = contentState,
|
||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||
directLogoutState = directLogoutState,
|
||||
hideInvitesAvatars = hideInvitesAvatars,
|
||||
canReportRoom = canReportRoom,
|
||||
eventSink = eventSink,
|
||||
|
|
@ -8,8 +8,8 @@
|
|||
package io.element.android.features.home.impl.search
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.home.impl.aRoomListRoomSummaryList
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.home.impl.roomlist.aRoomListRoomSummaryList
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.home.impl.RoomListEvents
|
||||
import io.element.android.features.home.impl.components.RoomSummaryRow
|
||||
import io.element.android.features.home.impl.contentType
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.home.impl.roomlist.RoomListEvents
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.modifiers.applyIf
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
|
||||
import io.element.android.libraries.androidutils.system.DateTimeObserver
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
||||
class FakeDateTimeObserver : DateTimeObserver {
|
||||
override val changes = MutableSharedFlow<DateTimeObserver.Event>(extraBufferCapacity = 1)
|
||||
|
||||
fun given(event: DateTimeObserver.Event) {
|
||||
changes.tryEmit(event)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.home.impl.roomlist.aRoomListState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.indicator.api.IndicatorService
|
||||
import io.element.android.libraries.indicator.test.FakeIndicatorService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class HomePresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - should start with no user and then load user with success`() = runTest {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
userDisplayName = null,
|
||||
userAvatarUrl = null,
|
||||
)
|
||||
matrixClient.givenGetProfileResult(matrixClient.sessionId, Result.success(MatrixUser(matrixClient.sessionId, A_USER_NAME, AN_AVATAR_URL)))
|
||||
val presenter = createHomePresenter(
|
||||
client = matrixClient,
|
||||
rageshakeFeatureAvailability = { false },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.matrixUser).isEqualTo(MatrixUser(A_USER_ID))
|
||||
assertThat(initialState.canReportBug).isFalse()
|
||||
val withUserState = awaitItem()
|
||||
assertThat(withUserState.matrixUser.userId).isEqualTo(A_USER_ID)
|
||||
assertThat(withUserState.matrixUser.displayName).isEqualTo(A_USER_NAME)
|
||||
assertThat(withUserState.matrixUser.avatarUrl).isEqualTo(AN_AVATAR_URL)
|
||||
assertThat(withUserState.showAvatarIndicator).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - show avatar indicator`() = runTest {
|
||||
val indicatorService = FakeIndicatorService()
|
||||
val presenter = createHomePresenter(
|
||||
indicatorService = indicatorService,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.showAvatarIndicator).isFalse()
|
||||
indicatorService.setShowRoomListTopBarIndicator(true)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.showAvatarIndicator).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - should start with no user and then load user with error`() = runTest {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
userDisplayName = null,
|
||||
userAvatarUrl = null,
|
||||
)
|
||||
matrixClient.givenGetProfileResult(matrixClient.sessionId, Result.failure(AN_EXCEPTION))
|
||||
val presenter = createHomePresenter(client = matrixClient)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.matrixUser).isEqualTo(MatrixUser(matrixClient.sessionId))
|
||||
// No new state is coming
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createHomePresenter(
|
||||
client: MatrixClient = FakeMatrixClient(),
|
||||
syncService: SyncService = FakeSyncService(),
|
||||
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
|
||||
rageshakeFeatureAvailability: RageshakeFeatureAvailability = RageshakeFeatureAvailability { true },
|
||||
indicatorService: IndicatorService = FakeIndicatorService(),
|
||||
) = HomePresenter(
|
||||
client = client,
|
||||
syncService = syncService,
|
||||
snackbarDispatcher = snackbarDispatcher,
|
||||
indicatorService = indicatorService,
|
||||
logoutPresenter = { aDirectLogoutState() },
|
||||
roomListPresenter = { aRoomListState() },
|
||||
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
|
||||
)
|
||||
}
|
||||
|
|
@ -5,12 +5,13 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.home.impl.R
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureCalledOnceWithParam
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
|
|
@ -5,13 +5,14 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.Interaction
|
||||
import io.element.android.features.home.impl.FakeDateTimeObserver
|
||||
import io.element.android.features.home.impl.datasource.RoomListDataSource
|
||||
import io.element.android.features.home.impl.datasource.aRoomListRoomSummaryFactory
|
||||
import io.element.android.features.home.impl.filters.RoomListFiltersState
|
||||
|
|
@ -27,20 +28,14 @@ import io.element.android.features.invite.test.InMemorySeenInvitesStore
|
|||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
import io.element.android.features.leaveroom.api.aLeaveRoomState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.androidutils.system.DateTimeObserver
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.dateformatter.api.DateFormatter
|
||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState
|
||||
import io.element.android.libraries.indicator.api.IndicatorService
|
||||
import io.element.android.libraries.indicator.test.FakeIndicatorService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
|
@ -48,18 +43,12 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
|||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_3
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
|
|
@ -88,7 +77,6 @@ import io.element.android.tests.testutils.lambda.value
|
|||
import io.element.android.tests.testutils.test
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceTimeBy
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -100,65 +88,6 @@ class RoomListPresenterTest {
|
|||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - should start with no user and then load user with success`() = runTest {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
userDisplayName = null,
|
||||
userAvatarUrl = null,
|
||||
)
|
||||
matrixClient.givenGetProfileResult(matrixClient.sessionId, Result.success(MatrixUser(matrixClient.sessionId, A_USER_NAME, AN_AVATAR_URL)))
|
||||
val presenter = createRoomListPresenter(
|
||||
client = matrixClient,
|
||||
rageshakeFeatureAvailability = { false },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.matrixUser).isEqualTo(MatrixUser(A_USER_ID))
|
||||
assertThat(initialState.canReportBug).isFalse()
|
||||
val withUserState = awaitItem()
|
||||
assertThat(withUserState.matrixUser.userId).isEqualTo(A_USER_ID)
|
||||
assertThat(withUserState.matrixUser.displayName).isEqualTo(A_USER_NAME)
|
||||
assertThat(withUserState.matrixUser.avatarUrl).isEqualTo(AN_AVATAR_URL)
|
||||
assertThat(withUserState.showAvatarIndicator).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - show avatar indicator`() = runTest {
|
||||
val indicatorService = FakeIndicatorService()
|
||||
val presenter = createRoomListPresenter(
|
||||
indicatorService = indicatorService,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.showAvatarIndicator).isFalse()
|
||||
indicatorService.setShowRoomListTopBarIndicator(true)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.showAvatarIndicator).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - should start with no user and then load user with error`() = runTest {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
userDisplayName = null,
|
||||
userAvatarUrl = null,
|
||||
)
|
||||
matrixClient.givenGetProfileResult(matrixClient.sessionId, Result.failure(AN_EXCEPTION))
|
||||
val presenter = createRoomListPresenter(client = matrixClient)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.matrixUser).isEqualTo(MatrixUser(matrixClient.sessionId))
|
||||
// No new state is coming
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - load 1 room with success`() = runTest {
|
||||
val roomListService = FakeRoomListService()
|
||||
|
|
@ -672,8 +601,6 @@ class RoomListPresenterTest {
|
|||
|
||||
private fun TestScope.createRoomListPresenter(
|
||||
client: MatrixClient = FakeMatrixClient(),
|
||||
syncService: SyncService = FakeSyncService(),
|
||||
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
|
||||
leaveRoomState: LeaveRoomState = aLeaveRoomState(),
|
||||
dateFormatter: DateFormatter = FakeDateFormatter(),
|
||||
roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(),
|
||||
|
|
@ -685,13 +612,9 @@ class RoomListPresenterTest {
|
|||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() },
|
||||
notificationCleaner: NotificationCleaner = FakeNotificationCleaner(),
|
||||
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
rageshakeFeatureAvailability: RageshakeFeatureAvailability = RageshakeFeatureAvailability { true },
|
||||
seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(),
|
||||
indicatorService: IndicatorService = FakeIndicatorService(),
|
||||
) = RoomListPresenter(
|
||||
client = client,
|
||||
syncService = syncService,
|
||||
snackbarDispatcher = snackbarDispatcher,
|
||||
leaveRoomPresenter = { leaveRoomState },
|
||||
roomListDataSource = RoomListDataSource(
|
||||
roomListService = client.roomListService,
|
||||
|
|
@ -705,7 +628,6 @@ class RoomListPresenterTest {
|
|||
dateTimeObserver = FakeDateTimeObserver(),
|
||||
),
|
||||
featureFlagService = featureFlagService,
|
||||
indicatorService = indicatorService,
|
||||
searchPresenter = searchPresenter,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
filtersPresenter = filtersPresenter,
|
||||
|
|
@ -714,17 +636,7 @@ class RoomListPresenterTest {
|
|||
fullScreenIntentPermissionsPresenter = { aFullScreenIntentPermissionsState() },
|
||||
batteryOptimizationPresenter = { aBatteryOptimizationState() },
|
||||
notificationCleaner = notificationCleaner,
|
||||
logoutPresenter = { aDirectLogoutState() },
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
|
||||
seenInvitesStore = seenInvitesStore,
|
||||
)
|
||||
}
|
||||
|
||||
class FakeDateTimeObserver : DateTimeObserver {
|
||||
override val changes = MutableSharedFlow<DateTimeObserver.Event>(extraBufferCapacity = 1)
|
||||
|
||||
fun given(event: DateTimeObserver.Event) {
|
||||
changes.tryEmit(event)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,6 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
internal fun RoomListState.contentAsRooms() = contentState as RoomListContentState.Rooms
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.home.impl
|
||||
package io.element.android.features.home.impl.roomlist
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
|
|
@ -16,6 +16,9 @@ import androidx.compose.ui.test.onNodeWithText
|
|||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.home.impl.HomeView
|
||||
import io.element.android.features.home.impl.R
|
||||
import io.element.android.features.home.impl.aHomeState
|
||||
import io.element.android.features.home.impl.components.RoomListMenuAction
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.home.impl.model.RoomSummaryDisplayType
|
||||
|
|
@ -35,7 +38,8 @@ import org.junit.runner.RunWith
|
|||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class RoomListViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
@get:Rule
|
||||
val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `displaying the view automatically sends a couple of UpdateVisibleRangeEvents`() {
|
||||
|
|
@ -272,8 +276,8 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomL
|
|||
onDeclineInviteAndBlockUser: (RoomListRoomSummary) -> Unit = EnsureNeverCalledWithParam(),
|
||||
) {
|
||||
setSafeContent {
|
||||
RoomListView(
|
||||
state = state,
|
||||
HomeView(
|
||||
homeState = aHomeState(roomListState = state),
|
||||
onRoomClick = onRoomClick,
|
||||
onSettingsClick = onSettingsClick,
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7b0dc90002ec59917ed0c64dae785088117c5aca61961c3d71dc7c05d2fa72b
|
||||
size 82238
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ca1e0f43d447faf770ee7aa4f443f3a310f520c9437cbd5baca9ac7ce36d2aae
|
||||
size 88613
|
||||
Loading…
Add table
Add a link
Reference in a new issue