mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-12-18 07:11:58 +03:00
Merge pull request #8159 from vector-im/feature/mna/aggregated-unread-indicator
Add aggregated unread indicator for spaces in the new layout
This commit is contained in:
commit
a6f7302350
18 changed files with 790 additions and 39 deletions
1
changelog.d/8157.feature
Normal file
1
changelog.d/8157.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add aggregated unread indicator for spaces in the new layout
|
|
@ -40,6 +40,7 @@ import im.vector.app.features.discovery.DiscoverySettingsViewModel
|
||||||
import im.vector.app.features.discovery.change.SetIdentityServerViewModel
|
import im.vector.app.features.discovery.change.SetIdentityServerViewModel
|
||||||
import im.vector.app.features.home.HomeActivityViewModel
|
import im.vector.app.features.home.HomeActivityViewModel
|
||||||
import im.vector.app.features.home.HomeDetailViewModel
|
import im.vector.app.features.home.HomeDetailViewModel
|
||||||
|
import im.vector.app.features.home.NewHomeDetailViewModel
|
||||||
import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel
|
import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel
|
||||||
import im.vector.app.features.home.UnreadMessagesSharedViewModel
|
import im.vector.app.features.home.UnreadMessagesSharedViewModel
|
||||||
import im.vector.app.features.home.UserColorAccountDataViewModel
|
import im.vector.app.features.home.UserColorAccountDataViewModel
|
||||||
|
@ -717,4 +718,9 @@ interface MavericksViewModelModule {
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(RoomPollDetailViewModel::class)
|
@MavericksViewModelKey(RoomPollDetailViewModel::class)
|
||||||
fun roomPollDetailViewModelFactory(factory: RoomPollDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun roomPollDetailViewModelFactory(factory: RoomPollDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(NewHomeDetailViewModel::class)
|
||||||
|
fun newHomeDetailViewModelFactory(factory: NewHomeDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.home
|
||||||
|
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.features.spaces.GetSpacesUseCase
|
||||||
|
import im.vector.app.features.spaces.notification.GetNotificationCountForSpacesUseCase
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetSpacesNotificationBadgeStateUseCase @Inject constructor(
|
||||||
|
private val getNotificationCountForSpacesUseCase: GetNotificationCountForSpacesUseCase,
|
||||||
|
private val getSpacesUseCase: GetSpacesUseCase,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun execute(): Flow<UnreadCounterBadgeView.State> {
|
||||||
|
val params = spaceSummaryQueryParams {
|
||||||
|
memberships = listOf(Membership.INVITE)
|
||||||
|
displayName = QueryStringValue.IsNotEmpty
|
||||||
|
}
|
||||||
|
return combine(
|
||||||
|
getNotificationCountForSpacesUseCase.execute(SpaceFilter.NoFilter),
|
||||||
|
getSpacesUseCase.execute(params),
|
||||||
|
) { spacesNotificationCount, spaceInvites ->
|
||||||
|
computeSpacesNotificationCounterBadgeState(spacesNotificationCount, spaceInvites)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeSpacesNotificationCounterBadgeState(
|
||||||
|
spacesNotificationCount: RoomAggregateNotificationCount,
|
||||||
|
spaceInvites: List<RoomSummary>,
|
||||||
|
): UnreadCounterBadgeView.State {
|
||||||
|
val hasPendingSpaceInvites = spaceInvites.isNotEmpty()
|
||||||
|
return if (hasPendingSpaceInvites && spacesNotificationCount.notificationCount == 0) {
|
||||||
|
UnreadCounterBadgeView.State.Text(
|
||||||
|
text = "!",
|
||||||
|
highlighted = true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
UnreadCounterBadgeView.State.Count(
|
||||||
|
count = spacesNotificationCount.notificationCount,
|
||||||
|
highlighted = spacesNotificationCount.isHighlight || hasPendingSpaceInvites,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ import im.vector.app.features.call.SharedKnownCallsViewModel
|
||||||
import im.vector.app.features.call.VectorCallActivity
|
import im.vector.app.features.call.VectorCallActivity
|
||||||
import im.vector.app.features.call.dialpad.PstnDialActivity
|
import im.vector.app.features.call.dialpad.PstnDialActivity
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
||||||
import im.vector.app.features.home.room.list.home.HomeRoomListFragment
|
import im.vector.app.features.home.room.list.home.HomeRoomListFragment
|
||||||
|
@ -82,6 +83,7 @@ class NewHomeDetailFragment :
|
||||||
@Inject lateinit var buildMeta: BuildMeta
|
@Inject lateinit var buildMeta: BuildMeta
|
||||||
|
|
||||||
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
||||||
|
private val newHomeDetailViewModel: NewHomeDetailViewModel by fragmentViewModel()
|
||||||
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
|
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
|
||||||
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by activityViewModel()
|
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by activityViewModel()
|
||||||
|
|
||||||
|
@ -180,6 +182,10 @@ class NewHomeDetailFragment :
|
||||||
currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls())
|
currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls())
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newHomeDetailViewModel.onEach { viewState ->
|
||||||
|
refreshUnreadCounterBadge(viewState.spacesNotificationCounterBadgeState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
|
@ -379,6 +385,10 @@ class NewHomeDetailFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshUnreadCounterBadge(badgeState: UnreadCounterBadgeView.State) {
|
||||||
|
views.spacesUnreadCounterBadge.render(badgeState)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTapToReturnToCall() {
|
override fun onTapToReturnToCall() {
|
||||||
callManager.getCurrentCall()?.let { call ->
|
callManager.getCurrentCall()?.let { call ->
|
||||||
VectorCallActivity.newIntent(
|
VectorCallActivity.newIntent(
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.home
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
|
import im.vector.app.core.platform.EmptyAction
|
||||||
|
import im.vector.app.core.platform.EmptyViewEvents
|
||||||
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
class NewHomeDetailViewModel @AssistedInject constructor(
|
||||||
|
@Assisted initialState: NewHomeDetailViewState,
|
||||||
|
private val getSpacesNotificationBadgeStateUseCase: GetSpacesNotificationBadgeStateUseCase,
|
||||||
|
) : VectorViewModel<NewHomeDetailViewState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : MavericksAssistedViewModelFactory<NewHomeDetailViewModel, NewHomeDetailViewState> {
|
||||||
|
override fun create(initialState: NewHomeDetailViewState): NewHomeDetailViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<NewHomeDetailViewModel, NewHomeDetailViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
init {
|
||||||
|
observeSpacesNotificationBadgeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeSpacesNotificationBadgeState() {
|
||||||
|
getSpacesNotificationBadgeStateUseCase.execute()
|
||||||
|
.onEach { badgeState -> setState { copy(spacesNotificationCounterBadgeState = badgeState) } }
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(action: EmptyAction) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.home
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.MavericksState
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
|
||||||
|
data class NewHomeDetailViewState(
|
||||||
|
val spacesNotificationCounterBadgeState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State.Count(count = 0, highlighted = false),
|
||||||
|
) : MavericksState
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.spaces
|
||||||
|
|
||||||
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||||
|
import org.matrix.android.sdk.flow.flow
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetSpacesUseCase @Inject constructor(
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun execute(params: SpaceSummaryQueryParams): Flow<List<RoomSummary>> {
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
|
|
||||||
|
return session?.flow()?.liveSpaceSummaries(params) ?: emptyFlow()
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,16 +29,13 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.analytics.AnalyticsTracker
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
import im.vector.app.features.analytics.plan.Interaction
|
import im.vector.app.features.analytics.plan.Interaction
|
||||||
import im.vector.app.features.invite.AutoAcceptInvites
|
|
||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import kotlinx.coroutines.Dispatchers
|
import im.vector.app.features.spaces.notification.GetNotificationCountForSpacesUseCase
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.sample
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
@ -48,25 +45,22 @@ import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.getUserOrDefault
|
import org.matrix.android.sdk.api.session.getUserOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
|
||||||
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
|
||||||
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
|
||||||
import org.matrix.android.sdk.api.session.space.SpaceOrderUtils
|
import org.matrix.android.sdk.api.session.space.SpaceOrderUtils
|
||||||
import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent
|
import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent
|
||||||
import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator
|
import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import org.matrix.android.sdk.flow.flow
|
|
||||||
|
|
||||||
class SpaceListViewModel @AssistedInject constructor(
|
class SpaceListViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: SpaceListViewState,
|
@Assisted initialState: SpaceListViewState,
|
||||||
private val spaceStateHandler: SpaceStateHandler,
|
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 analyticsTracker: AnalyticsTracker,
|
private val analyticsTracker: AnalyticsTracker,
|
||||||
|
getNotificationCountForSpacesUseCase: GetNotificationCountForSpacesUseCase,
|
||||||
|
private val getSpacesUseCase: GetSpacesUseCase,
|
||||||
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {
|
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -92,39 +86,14 @@ class SpaceListViewModel @AssistedInject constructor(
|
||||||
copy(selectedSpace = selectedSpaceOption.orNull())
|
copy(selectedSpace = selectedSpaceOption.orNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX there should be a way to refactor this and share it
|
getNotificationCountForSpacesUseCase.execute(roomsInSpaceFilter())
|
||||||
session.roomService().getPagedRoomSummariesLive(
|
.onEach { counts ->
|
||||||
roomSummaryQueryParams {
|
|
||||||
this.memberships = listOf(Membership.JOIN)
|
|
||||||
this.spaceFilter = roomsInSpaceFilter()
|
|
||||||
}, sortOrder = RoomSortOrder.NONE
|
|
||||||
).asFlow()
|
|
||||||
.sample(300)
|
|
||||||
.onEach {
|
|
||||||
val inviteCount = if (autoAcceptInvites.hideInvites) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
session.roomService().getRoomSummaries(
|
|
||||||
roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) }
|
|
||||||
).size
|
|
||||||
}
|
|
||||||
val totalCount = session.roomService().getNotificationCountForRooms(
|
|
||||||
roomSummaryQueryParams {
|
|
||||||
this.memberships = listOf(Membership.JOIN)
|
|
||||||
this.spaceFilter = roomsInSpaceFilter()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
val counts = RoomAggregateNotificationCount(
|
|
||||||
totalCount.notificationCount + inviteCount,
|
|
||||||
totalCount.highlightCount + inviteCount
|
|
||||||
)
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
homeAggregateCount = counts
|
homeAggregateCount = counts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flowOn(Dispatchers.Default)
|
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +236,7 @@ class SpaceListViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
combine(
|
combine(
|
||||||
session.flow().liveSpaceSummaries(params),
|
getSpacesUseCase.execute(params),
|
||||||
session.accountDataService()
|
session.accountDataService()
|
||||||
.getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER))
|
.getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER))
|
||||||
.asFlow()
|
.asFlow()
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.spaces.notification
|
||||||
|
|
||||||
|
import androidx.lifecycle.asFlow
|
||||||
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.app.features.invite.AutoAcceptInvites
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
|
import kotlinx.coroutines.flow.sample
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetNotificationCountForSpacesUseCase @Inject constructor(
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
private val autoAcceptInvites: AutoAcceptInvites,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun execute(spaceFilter: SpaceFilter): Flow<RoomAggregateNotificationCount> {
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
|
|
||||||
|
val spaceQueryParams = roomSummaryQueryParams {
|
||||||
|
this.memberships = listOf(Membership.JOIN)
|
||||||
|
this.spaceFilter = spaceFilter
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
?.roomService()
|
||||||
|
?.getPagedRoomSummariesLive(queryParams = spaceQueryParams, sortOrder = RoomSortOrder.NONE)
|
||||||
|
?.asFlow()
|
||||||
|
?.sample(300)
|
||||||
|
?.mapLatest {
|
||||||
|
val inviteCount = if (autoAcceptInvites.hideInvites) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
session.roomService().getRoomSummaries(
|
||||||
|
roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) }
|
||||||
|
).size
|
||||||
|
}
|
||||||
|
val totalCount = session.roomService().getNotificationCountForRooms(spaceQueryParams)
|
||||||
|
RoomAggregateNotificationCount(
|
||||||
|
notificationCount = totalCount.notificationCount + inviteCount,
|
||||||
|
highlightCount = totalCount.highlightCount + inviteCount,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?.flowOn(session.coroutineDispatchers.main)
|
||||||
|
?: emptyFlow()
|
||||||
|
}
|
||||||
|
}
|
|
@ -120,6 +120,28 @@
|
||||||
tools:targetApi="lollipop_mr1"
|
tools:targetApi="lollipop_mr1"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
android:id="@+id/spacesUnreadCounterBadge"
|
||||||
|
style="@style/Widget.Vector.TextView.Micro"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:elevation="15dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="16dp"
|
||||||
|
android:minHeight="16dp"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:textColor="?colorOnError"
|
||||||
|
app:layout_constraintCircle="@id/newLayoutOpenSpacesButton"
|
||||||
|
app:layout_constraintCircleAngle="45"
|
||||||
|
app:layout_constraintCircleRadius="24dp"
|
||||||
|
tools:background="@drawable/bg_unread_highlight"
|
||||||
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:text="147"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/newLayoutCreateChatButton"
|
android:id="@+id/newLayoutCreateChatButton"
|
||||||
style="@style/Widget.Vector.FloatingActionButton"
|
style="@style/Widget.Vector.FloatingActionButton"
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.home
|
||||||
|
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.features.spaces.GetSpacesUseCase
|
||||||
|
import im.vector.app.features.spaces.notification.GetNotificationCountForSpacesUseCase
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
|
||||||
|
internal class GetSpacesNotificationBadgeStateUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeGetNotificationCountForSpacesUseCase = mockk<GetNotificationCountForSpacesUseCase>()
|
||||||
|
private val fakeGetSpacesUseCase = mockk<GetSpacesUseCase>()
|
||||||
|
|
||||||
|
private val getSpacesNotificationBadgeStateUseCase = GetSpacesNotificationBadgeStateUseCase(
|
||||||
|
getNotificationCountForSpacesUseCase = fakeGetNotificationCountForSpacesUseCase,
|
||||||
|
getSpacesUseCase = fakeGetSpacesUseCase,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given flow of spaces invite and notification count then flow of state is correct`() = runTest {
|
||||||
|
// Given
|
||||||
|
val noSpacesInvite = emptyList<RoomSummary>()
|
||||||
|
val existingSpaceInvite = listOf<RoomSummary>(mockk())
|
||||||
|
val noNotification = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 0,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
val existingNotificationNotHighlighted = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
val existingNotificationHighlighted = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 1,
|
||||||
|
)
|
||||||
|
every { fakeGetSpacesUseCase.execute(any()) } returns
|
||||||
|
flowOf(noSpacesInvite, existingSpaceInvite, existingSpaceInvite, noSpacesInvite, noSpacesInvite)
|
||||||
|
every { fakeGetNotificationCountForSpacesUseCase.execute(any()) } returns
|
||||||
|
flowOf(noNotification, noNotification, existingNotificationNotHighlighted, existingNotificationNotHighlighted, existingNotificationHighlighted)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getSpacesNotificationBadgeStateUseCase.execute().test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
val expectedState1 = UnreadCounterBadgeView.State.Count(count = 0, highlighted = false)
|
||||||
|
val expectedState2 = UnreadCounterBadgeView.State.Text(text = "!", highlighted = true)
|
||||||
|
val expectedState3 = UnreadCounterBadgeView.State.Count(count = 1, highlighted = true)
|
||||||
|
val expectedState4 = UnreadCounterBadgeView.State.Count(count = 1, highlighted = false)
|
||||||
|
val expectedState5 = UnreadCounterBadgeView.State.Count(count = 1, highlighted = true)
|
||||||
|
testObserver
|
||||||
|
.assertValues(expectedState1, expectedState2, expectedState3, expectedState4, expectedState5)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeGetSpacesUseCase.execute(match {
|
||||||
|
it.memberships == listOf(Membership.INVITE) && it.displayName == QueryStringValue.IsNotEmpty
|
||||||
|
})
|
||||||
|
}
|
||||||
|
verify { fakeGetNotificationCountForSpacesUseCase.execute(SpaceFilter.NoFilter) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.home
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.test.MavericksTestRule
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
internal class NewHomeDetailViewModelTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val mavericksTestRule = MavericksTestRule(testDispatcher = UnconfinedTestDispatcher())
|
||||||
|
|
||||||
|
private val initialState = NewHomeDetailViewState()
|
||||||
|
private val fakeGetSpacesNotificationBadgeStateUseCase = mockk<GetSpacesNotificationBadgeStateUseCase>()
|
||||||
|
|
||||||
|
private fun createViewModel(): NewHomeDetailViewModel {
|
||||||
|
return NewHomeDetailViewModel(
|
||||||
|
initialState = initialState,
|
||||||
|
getSpacesNotificationBadgeStateUseCase = fakeGetSpacesNotificationBadgeStateUseCase,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given the viewModel is created then viewState is updated with space notifications badge state`() {
|
||||||
|
// Given
|
||||||
|
val aBadgeState = UnreadCounterBadgeView.State.Count(count = 1, highlighted = false)
|
||||||
|
every { fakeGetSpacesNotificationBadgeStateUseCase.execute() } returns flowOf(aBadgeState)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
val viewModelTest = viewModel.test()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
val expectedViewState = initialState.copy(
|
||||||
|
spacesNotificationCounterBadgeState = aBadgeState,
|
||||||
|
)
|
||||||
|
viewModelTest
|
||||||
|
.assertLatestState(expectedViewState)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeGetSpacesNotificationBadgeStateUseCase.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.spaces
|
||||||
|
|
||||||
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
|
import im.vector.app.test.fakes.FakeFlowLiveDataConversions
|
||||||
|
import im.vector.app.test.fakes.givenAsFlow
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||||
|
|
||||||
|
internal class GetSpacesUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
|
private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions()
|
||||||
|
|
||||||
|
private val getSpacesUseCase = GetSpacesUseCase(
|
||||||
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
fakeFlowLiveDataConversions.setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given params when execute then the list of summaries is returned`() = runTest {
|
||||||
|
// Given
|
||||||
|
val queryParams = givenSpaceQueryParams()
|
||||||
|
val firstSummaries = listOf<RoomSummary>(mockk())
|
||||||
|
val nextSummaries = listOf<RoomSummary>(mockk())
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeSpaceService
|
||||||
|
.givenGetSpaceSummariesReturns(firstSummaries)
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeSpaceService
|
||||||
|
.givenGetSpaceSummariesLiveReturns(nextSummaries)
|
||||||
|
.givenAsFlow()
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getSpacesUseCase.execute(queryParams).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertValues(firstSummaries, nextSummaries)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummaries(queryParams)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummariesLive(queryParams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given no active session when execute then empty flow is returned`() = runTest {
|
||||||
|
// Given
|
||||||
|
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
|
||||||
|
val queryParams = givenSpaceQueryParams()
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getSpacesUseCase.execute(queryParams).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertNoValues()
|
||||||
|
.finish()
|
||||||
|
verify(inverse = true) {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummaries(queryParams)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummariesLive(queryParams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenSpaceQueryParams(): SpaceSummaryQueryParams {
|
||||||
|
return mockk()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.spaces.notification
|
||||||
|
|
||||||
|
import androidx.paging.PagedList
|
||||||
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
|
import im.vector.app.test.fakes.FakeAutoAcceptInvites
|
||||||
|
import im.vector.app.test.fakes.FakeFlowLiveDataConversions
|
||||||
|
import im.vector.app.test.fakes.givenAsFlow
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.sample
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
|
||||||
|
internal class GetNotificationCountForSpacesUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
|
private val fakeAutoAcceptInvites = FakeAutoAcceptInvites()
|
||||||
|
private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions()
|
||||||
|
|
||||||
|
private val getNotificationCountForSpacesUseCase = GetNotificationCountForSpacesUseCase(
|
||||||
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
|
autoAcceptInvites = fakeAutoAcceptInvites,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
fakeFlowLiveDataConversions.setup()
|
||||||
|
mockkStatic("kotlinx.coroutines.flow.FlowKt")
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given space filter and auto accept invites when execute then correct notification count is returned`() = runTest {
|
||||||
|
// given
|
||||||
|
val spaceFilter = SpaceFilter.NoFilter
|
||||||
|
val pagedList = mockk<PagedList<RoomSummary>>()
|
||||||
|
val pagedListFlow = fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetPagedRoomSummariesLiveReturns(pagedList)
|
||||||
|
.givenAsFlow()
|
||||||
|
every { pagedListFlow.sample(any<Long>()) } returns pagedListFlow
|
||||||
|
val expectedNotificationCount = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetNotificationCountForRoomsReturns(expectedNotificationCount)
|
||||||
|
fakeAutoAcceptInvites._isEnabled = true
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getNotificationCountForSpacesUseCase.execute(spaceFilter).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertValues(expectedNotificationCount)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getNotificationCountForRooms(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter }
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getPagedRoomSummariesLive(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter },
|
||||||
|
pagedListConfig = any(),
|
||||||
|
sortOrder = RoomSortOrder.NONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given space filter and show invites when execute then correct notification count is returned`() = runTest {
|
||||||
|
// given
|
||||||
|
val spaceFilter = SpaceFilter.NoFilter
|
||||||
|
val pagedList = mockk<PagedList<RoomSummary>>()
|
||||||
|
val pagedListFlow = fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetPagedRoomSummariesLiveReturns(pagedList)
|
||||||
|
.givenAsFlow()
|
||||||
|
every { pagedListFlow.sample(any<Long>()) } returns pagedListFlow
|
||||||
|
val notificationCount = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetNotificationCountForRoomsReturns(notificationCount)
|
||||||
|
val invitedRooms = listOf<RoomSummary>(mockk())
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetRoomSummaries(invitedRooms)
|
||||||
|
fakeAutoAcceptInvites._isEnabled = false
|
||||||
|
val expectedNotificationCount = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = notificationCount.notificationCount + invitedRooms.size,
|
||||||
|
highlightCount = notificationCount.highlightCount + invitedRooms.size,
|
||||||
|
)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getNotificationCountForSpacesUseCase.execute(spaceFilter).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertValues(expectedNotificationCount)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getRoomSummaries(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.INVITE) }
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getNotificationCountForRooms(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter }
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getPagedRoomSummariesLive(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter },
|
||||||
|
pagedListConfig = any(),
|
||||||
|
sortOrder = RoomSortOrder.NONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given no active session when execute then empty flow is returned`() = runTest {
|
||||||
|
// given
|
||||||
|
val spaceFilter = SpaceFilter.NoFilter
|
||||||
|
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getNotificationCountForSpacesUseCase.execute(spaceFilter).test(this)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertNoValues()
|
||||||
|
.finish()
|
||||||
|
verify(inverse = true) {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getPagedRoomSummariesLive(
|
||||||
|
queryParams = any(),
|
||||||
|
pagedListConfig = any(),
|
||||||
|
sortOrder = any(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
class FakeFlowLiveDataConversions {
|
class FakeFlowLiveDataConversions {
|
||||||
|
@ -28,6 +29,8 @@ class FakeFlowLiveDataConversions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> LiveData<T>.givenAsFlow() {
|
fun <T> LiveData<T>.givenAsFlow(): Flow<T> {
|
||||||
every { asFlow() } returns flowOf(value!!)
|
return flowOf(value!!).also {
|
||||||
|
every { asFlow() } returns it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,14 @@
|
||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.paging.PagedList
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.matrix.android.sdk.api.session.room.RoomService
|
import org.matrix.android.sdk.api.session.room.RoomService
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
|
||||||
class FakeRoomService(
|
class FakeRoomService(
|
||||||
private val fakeRoom: FakeRoom = FakeRoom()
|
private val fakeRoom: FakeRoom = FakeRoom()
|
||||||
|
@ -34,4 +38,18 @@ class FakeRoomService(
|
||||||
fun set(roomSummary: RoomSummary?) {
|
fun set(roomSummary: RoomSummary?) {
|
||||||
every { getRoomSummary(any()) } returns roomSummary
|
every { getRoomSummary(any()) } returns roomSummary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenGetPagedRoomSummariesLiveReturns(pagedList: PagedList<RoomSummary>): LiveData<PagedList<RoomSummary>> {
|
||||||
|
return MutableLiveData(pagedList).also {
|
||||||
|
every { getPagedRoomSummariesLive(queryParams = any(), pagedListConfig = any(), sortOrder = any()) } returns it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenGetNotificationCountForRoomsReturns(roomAggregateNotificationCount: RoomAggregateNotificationCount) {
|
||||||
|
every { getNotificationCountForRooms(queryParams = any()) } returns roomAggregateNotificationCount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenGetRoomSummaries(roomSummaries: List<RoomSummary>) {
|
||||||
|
every { getRoomSummaries(any()) } returns roomSummaries
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ class FakeSession(
|
||||||
val fakeUserService: FakeUserService = FakeUserService(),
|
val fakeUserService: FakeUserService = FakeUserService(),
|
||||||
private val fakeEventService: FakeEventService = FakeEventService(),
|
private val fakeEventService: FakeEventService = FakeEventService(),
|
||||||
val fakeSessionAccountDataService: FakeSessionAccountDataService = FakeSessionAccountDataService(),
|
val fakeSessionAccountDataService: FakeSessionAccountDataService = FakeSessionAccountDataService(),
|
||||||
|
val fakeSpaceService: FakeSpaceService = FakeSpaceService(),
|
||||||
) : Session by mockk(relaxed = true) {
|
) : Session by mockk(relaxed = true) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -66,6 +67,7 @@ class FakeSession(
|
||||||
override fun pushersService() = fakePushersService
|
override fun pushersService() = fakePushersService
|
||||||
override fun accountDataService() = fakeSessionAccountDataService
|
override fun accountDataService() = fakeSessionAccountDataService
|
||||||
override fun userService() = fakeUserService
|
override fun userService() = fakeUserService
|
||||||
|
override fun spaceService() = fakeSpaceService
|
||||||
|
|
||||||
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
|
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
|
||||||
coEvery {
|
coEvery {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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 androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.space.SpaceService
|
||||||
|
|
||||||
|
class FakeSpaceService : SpaceService by mockk() {
|
||||||
|
|
||||||
|
fun givenGetSpaceSummariesLiveReturns(roomSummaries: List<RoomSummary>): LiveData<List<RoomSummary>> {
|
||||||
|
return MutableLiveData(roomSummaries).also {
|
||||||
|
every { getSpaceSummariesLive(any()) } returns it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenGetSpaceSummariesReturns(roomSummaries: List<RoomSummary>) {
|
||||||
|
every { getSpaceSummaries(any()) } returns roomSummaries
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue