mirror of
https://github.com/element-hq/element-android
synced 2024-11-26 11:25:40 +03:00
Extracting logic to compute the badge state into a usecase
This commit is contained in:
parent
f782a31592
commit
2bd0126523
6 changed files with 174 additions and 66 deletions
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,7 +64,6 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -185,7 +184,7 @@ class NewHomeDetailFragment :
|
|||
}
|
||||
|
||||
newHomeDetailViewModel.onEach { viewState ->
|
||||
refreshUnreadCounterBadge(viewState.spacesNotificationCount, viewState.hasPendingSpaceInvites)
|
||||
refreshUnreadCounterBadge(viewState.spacesNotificationCounterBadgeState)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,21 +385,7 @@ class NewHomeDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun refreshUnreadCounterBadge(
|
||||
spacesNotificationCount: RoomAggregateNotificationCount,
|
||||
hasPendingSpaceInvites: Boolean,
|
||||
) {
|
||||
val badgeState = if (hasPendingSpaceInvites && spacesNotificationCount.notificationCount == 0) {
|
||||
UnreadCounterBadgeView.State.Text(
|
||||
text = "!",
|
||||
highlighted = true,
|
||||
)
|
||||
} else {
|
||||
UnreadCounterBadgeView.State.Count(
|
||||
count = spacesNotificationCount.notificationCount,
|
||||
highlighted = spacesNotificationCount.isHighlight || hasPendingSpaceInvites,
|
||||
)
|
||||
}
|
||||
private fun refreshUnreadCounterBadge(badgeState: UnreadCounterBadgeView.State) {
|
||||
views.spacesUnreadCounterBadge.render(badgeState)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,19 +25,12 @@ 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 im.vector.app.features.spaces.GetSpacesUseCase
|
||||
import im.vector.app.features.spaces.notification.GetNotificationCountForSpacesUseCase
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
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.spaceSummaryQueryParams
|
||||
|
||||
class NewHomeDetailViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: NewHomeDetailViewState,
|
||||
private val getNotificationCountForSpacesUseCase: GetNotificationCountForSpacesUseCase,
|
||||
private val getSpacesUseCase: GetSpacesUseCase,
|
||||
private val getSpacesNotificationBadgeStateUseCase: GetSpacesNotificationBadgeStateUseCase,
|
||||
) : VectorViewModel<NewHomeDetailViewState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -48,23 +41,12 @@ class NewHomeDetailViewModel @AssistedInject constructor(
|
|||
companion object : MavericksViewModelFactory<NewHomeDetailViewModel, NewHomeDetailViewState> by hiltMavericksViewModelFactory()
|
||||
|
||||
init {
|
||||
observeSpacesNotificationCount()
|
||||
observeSpacesInvite()
|
||||
observeSpacesNotificationBadgeState()
|
||||
}
|
||||
|
||||
private fun observeSpacesNotificationCount() {
|
||||
getNotificationCountForSpacesUseCase.execute(SpaceFilter.NoFilter)
|
||||
.onEach { setState { copy(spacesNotificationCount = it) } }
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun observeSpacesInvite() {
|
||||
val params = spaceSummaryQueryParams {
|
||||
memberships = listOf(Membership.INVITE)
|
||||
displayName = QueryStringValue.IsNotEmpty
|
||||
}
|
||||
getSpacesUseCase.execute(params)
|
||||
.onEach { setState { copy(hasPendingSpaceInvites = it.isNotEmpty()) } }
|
||||
private fun observeSpacesNotificationBadgeState() {
|
||||
getSpacesNotificationBadgeStateUseCase.execute()
|
||||
.onEach { badgeState -> setState { copy(spacesNotificationCounterBadgeState = badgeState) } }
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
package im.vector.app.features.home
|
||||
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
|
||||
data class NewHomeDetailViewState(
|
||||
val spacesNotificationCount: RoomAggregateNotificationCount = RoomAggregateNotificationCount(notificationCount = 0, highlightCount = 0),
|
||||
val hasPendingSpaceInvites: Boolean = false,
|
||||
val spacesNotificationCounterBadgeState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State.Count(count = 0, highlighted = false),
|
||||
) : MavericksState
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
|
@ -17,8 +17,8 @@
|
|||
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.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
|
||||
|
@ -27,10 +27,6 @@ import kotlinx.coroutines.flow.flowOf
|
|||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
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 NewHomeDetailViewModelTest {
|
||||
|
||||
|
@ -38,43 +34,34 @@ internal class NewHomeDetailViewModelTest {
|
|||
val mavericksTestRule = MavericksTestRule(testDispatcher = UnconfinedTestDispatcher())
|
||||
|
||||
private val initialState = NewHomeDetailViewState()
|
||||
private val fakeGetNotificationCountForSpacesUseCase = mockk<GetNotificationCountForSpacesUseCase>()
|
||||
private val fakeGetSpacesUseCase = mockk<GetSpacesUseCase>()
|
||||
private val fakeGetSpacesNotificationBadgeStateUseCase = mockk<GetSpacesNotificationBadgeStateUseCase>()
|
||||
|
||||
private fun createViewModel(): NewHomeDetailViewModel {
|
||||
return NewHomeDetailViewModel(
|
||||
initialState = initialState,
|
||||
getNotificationCountForSpacesUseCase = fakeGetNotificationCountForSpacesUseCase,
|
||||
getSpacesUseCase = fakeGetSpacesUseCase,
|
||||
getSpacesNotificationBadgeStateUseCase = fakeGetSpacesNotificationBadgeStateUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given the viewModel is created then viewState is updated with space notifications count and pending space invites`() {
|
||||
fun `given the viewModel is created then viewState is updated with space notifications badge state`() {
|
||||
// Given
|
||||
val spacesNotificationCount = RoomAggregateNotificationCount(
|
||||
notificationCount = 1,
|
||||
highlightCount = 1,
|
||||
)
|
||||
every { fakeGetNotificationCountForSpacesUseCase.execute(any()) } returns flowOf(spacesNotificationCount)
|
||||
val spaceInvites = listOf<RoomSummary>(mockk())
|
||||
every { fakeGetSpacesUseCase.execute(any()) } returns flowOf(spaceInvites)
|
||||
val expectedViewState = initialState.copy(
|
||||
spacesNotificationCount = spacesNotificationCount,
|
||||
hasPendingSpaceInvites = true,
|
||||
)
|
||||
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 {
|
||||
fakeGetNotificationCountForSpacesUseCase.execute(SpaceFilter.NoFilter)
|
||||
fakeGetSpacesUseCase.execute(match { it.memberships == listOf(Membership.INVITE) })
|
||||
fakeGetSpacesNotificationBadgeStateUseCase.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue