Sort authors stream by news count

Fixes #393
pull/447/head
Simon Marquis 3 years ago
parent 9fa3dadcfd
commit 00fc9c29d2

@ -17,34 +17,49 @@
package com.google.samples.apps.nowinandroid.core.domain package com.google.samples.apps.nowinandroid.core.domain
import com.google.samples.apps.nowinandroid.core.data.repository.AuthorsRepository import com.google.samples.apps.nowinandroid.core.data.repository.AuthorsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor
import com.google.samples.apps.nowinandroid.core.model.data.Author
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
/** /**
* A use case which obtains a list of authors sorted alphabetically by name with their followed * A use case which obtains a list of authors sorted by descending news count and then alphabetically by name with their followed state.
* state.
*/ */
class GetSortedFollowableAuthorsStreamUseCase @Inject constructor( class GetSortedFollowableAuthorsStreamUseCase @Inject constructor(
private val authorsRepository: AuthorsRepository, private val authorsRepository: AuthorsRepository,
private val userDataRepository: UserDataRepository private val newsRepository: NewsRepository,
private val userDataRepository: UserDataRepository,
) { ) {
/** /**
* Returns a list of authors with their associated followed state sorted alphabetically by name. * Returns a list of authors with their associated followed state sorted by descending news count and then alphabetically by name.
*/ */
operator fun invoke(): Flow<List<FollowableAuthor>> = operator fun invoke(): Flow<List<FollowableAuthor>> =
combine( combine(
authorsRepository.getAuthorsStream(), authorsRepository.getAuthorsStream(),
userDataRepository.userDataStream newsRepository.getNewsResourcesStream(),
) { authors, userData -> userDataRepository.userDataStream,
) { authors, news, userData ->
val newsCountByAuthor: Map<Author, Long> = buildMap {
news.forEach { news ->
news.authors.forEach { author ->
compute(author) { _, count -> count?.inc() ?: 1 }
}
}
}
authors.map { author -> authors.map { author ->
FollowableAuthor( FollowableAuthor(
author = author, author = author,
isFollowed = author.id in userData.followedAuthors isFollowed = author.id in userData.followedAuthors
) )
} }.sortedWith(
.sortedBy { it.author.name } compareByDescending<FollowableAuthor> {
newsCountByAuthor[it.author]
}.thenBy {
it.author.name
}
)
} }
} }

@ -18,12 +18,16 @@ package com.google.samples.apps.nowinandroid.core.domain
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor
import com.google.samples.apps.nowinandroid.core.model.data.Author import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
import com.google.samples.apps.nowinandroid.core.testing.repository.TestAuthorsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestAuthorsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Instant
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -33,15 +37,18 @@ class GetSortedFollowableAuthorsStreamUseCaseTest {
val mainDispatcherRule = MainDispatcherRule() val mainDispatcherRule = MainDispatcherRule()
private val authorsRepository = TestAuthorsRepository() private val authorsRepository = TestAuthorsRepository()
private val newsRepository = TestNewsRepository()
private val userDataRepository = TestUserDataRepository() private val userDataRepository = TestUserDataRepository()
val useCase = GetSortedFollowableAuthorsStreamUseCase( val useCase = GetSortedFollowableAuthorsStreamUseCase(
authorsRepository = authorsRepository, authorsRepository = authorsRepository,
userDataRepository = userDataRepository newsRepository = newsRepository,
userDataRepository = userDataRepository,
) )
@Test @Test
fun whenFollowedAuthorsSupplied_sortedFollowableAuthorsAreReturned() = runTest { fun whenFollowedAuthorsSupplied_sortedFollowableAuthorsAreReturned() = runTest {
newsRepository.sendNewsResources(listOf(sampleNews1))
// Specify some authors which the user is following. // Specify some authors which the user is following.
userDataRepository.setFollowedAuthorIds(setOf(sampleAuthor1.id)) userDataRepository.setFollowedAuthorIds(setOf(sampleAuthor1.id))
@ -95,3 +102,16 @@ private val sampleAuthor3 =
) )
private val sampleAuthors = listOf(sampleAuthor1, sampleAuthor2, sampleAuthor3) private val sampleAuthors = listOf(sampleAuthor1, sampleAuthor2, sampleAuthor3)
private val sampleNews1 =
NewsResource(
id = "1",
title = "",
content = "",
url = "",
headerImageUrl = null,
publishDate = Instant.parse("2021-11-09T00:00:00.000Z"),
type = Video,
authors = listOf(sampleAuthor2),
topics = emptyList()
)

@ -65,6 +65,7 @@ class ForYouViewModelTest {
) )
private val getSortedFollowableAuthorsStream = GetSortedFollowableAuthorsStreamUseCase( private val getSortedFollowableAuthorsStream = GetSortedFollowableAuthorsStreamUseCase(
authorsRepository = authorsRepository, authorsRepository = authorsRepository,
newsRepository = newsRepository,
userDataRepository = userDataRepository userDataRepository = userDataRepository
) )
private val getFollowableTopicsStreamUseCase = GetFollowableTopicsStreamUseCase( private val getFollowableTopicsStreamUseCase = GetFollowableTopicsStreamUseCase(
@ -190,6 +191,7 @@ class ForYouViewModelTest {
userDataRepository.setFollowedTopicIds(emptySet()) userDataRepository.setFollowedTopicIds(emptySet())
authorsRepository.sendAuthors(sampleAuthors) authorsRepository.sendAuthors(sampleAuthors)
userDataRepository.setFollowedAuthorIds(emptySet()) userDataRepository.setFollowedAuthorIds(emptySet())
newsRepository.sendNewsResources(emptyList())
assertEquals( assertEquals(
OnboardingUiState.Shown( OnboardingUiState.Shown(
@ -329,8 +331,8 @@ class ForYouViewModelTest {
authors = listOf( authors = listOf(
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "0", id = "1",
name = "Android Dev", name = "Android Dev 2",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -340,8 +342,8 @@ class ForYouViewModelTest {
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "1", id = "0",
name = "Android Dev 2", name = "Android Dev",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -386,6 +388,7 @@ class ForYouViewModelTest {
userDataRepository.setFollowedAuthorIds(emptySet()) userDataRepository.setFollowedAuthorIds(emptySet())
topicsRepository.sendTopics(sampleTopics) topicsRepository.sendTopics(sampleTopics)
userDataRepository.setFollowedTopicIds(setOf("0", "1")) userDataRepository.setFollowedTopicIds(setOf("0", "1"))
newsRepository.sendNewsResources(emptyList())
viewModel.dismissOnboarding() viewModel.dismissOnboarding()
assertEquals( assertEquals(
@ -469,8 +472,8 @@ class ForYouViewModelTest {
authors = listOf( authors = listOf(
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "0", id = "1",
name = "Android Dev", name = "Android Dev 2",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -480,8 +483,8 @@ class ForYouViewModelTest {
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "1", id = "0",
name = "Android Dev 2", name = "Android Dev",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -553,8 +556,8 @@ class ForYouViewModelTest {
authors = listOf( authors = listOf(
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "0", id = "1",
name = "Android Dev", name = "Android Dev 2",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -564,8 +567,8 @@ class ForYouViewModelTest {
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "1", id = "0",
name = "Android Dev 2", name = "Android Dev",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -660,8 +663,8 @@ class ForYouViewModelTest {
authors = listOf( authors = listOf(
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "0", id = "1",
name = "Android Dev", name = "Android Dev 2",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -671,8 +674,8 @@ class ForYouViewModelTest {
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "1", id = "0",
name = "Android Dev 2", name = "Android Dev",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -744,25 +747,25 @@ class ForYouViewModelTest {
authors = listOf( authors = listOf(
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "0", id = "1",
name = "Android Dev", name = "Android Dev 2",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
bio = "", bio = "",
), ),
isFollowed = false isFollowed = true
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "1", id = "0",
name = "Android Dev 2", name = "Android Dev",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
bio = "", bio = "",
), ),
isFollowed = true isFollowed = false
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
@ -854,8 +857,8 @@ class ForYouViewModelTest {
authors = listOf( authors = listOf(
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "0", id = "1",
name = "Android Dev", name = "Android Dev 2",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -865,8 +868,8 @@ class ForYouViewModelTest {
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "1", id = "0",
name = "Android Dev 2", name = "Android Dev",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -955,8 +958,8 @@ class ForYouViewModelTest {
authors = listOf( authors = listOf(
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "0", id = "1",
name = "Android Dev", name = "Android Dev 2",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",
@ -966,8 +969,8 @@ class ForYouViewModelTest {
), ),
FollowableAuthor( FollowableAuthor(
author = Author( author = Author(
id = "1", id = "0",
name = "Android Dev 2", name = "Android Dev",
imageUrl = "", imageUrl = "",
twitter = "", twitter = "",
mediumPage = "", mediumPage = "",

@ -23,6 +23,7 @@ import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.Author import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.testing.repository.TestAuthorsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestAuthorsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule
@ -48,6 +49,7 @@ class InterestsViewModelTest {
private val userDataRepository = TestUserDataRepository() private val userDataRepository = TestUserDataRepository()
private val authorsRepository = TestAuthorsRepository() private val authorsRepository = TestAuthorsRepository()
private val newsRepository = TestNewsRepository()
private val topicsRepository = TestTopicsRepository() private val topicsRepository = TestTopicsRepository()
private val getFollowableTopicsStreamUseCase = GetFollowableTopicsStreamUseCase( private val getFollowableTopicsStreamUseCase = GetFollowableTopicsStreamUseCase(
topicsRepository = topicsRepository, topicsRepository = topicsRepository,
@ -56,6 +58,7 @@ class InterestsViewModelTest {
private val getSortedFollowableAuthorsStream = private val getSortedFollowableAuthorsStream =
GetSortedFollowableAuthorsStreamUseCase( GetSortedFollowableAuthorsStreamUseCase(
authorsRepository = authorsRepository, authorsRepository = authorsRepository,
newsRepository = newsRepository,
userDataRepository = userDataRepository userDataRepository = userDataRepository
) )
private lateinit var viewModel: InterestsViewModel private lateinit var viewModel: InterestsViewModel
@ -67,6 +70,7 @@ class InterestsViewModelTest {
getFollowableTopicsStream = getFollowableTopicsStreamUseCase, getFollowableTopicsStream = getFollowableTopicsStreamUseCase,
getSortedFollowableAuthorsStream = getSortedFollowableAuthorsStream getSortedFollowableAuthorsStream = getSortedFollowableAuthorsStream
) )
newsRepository.sendNewsResources(emptyList())
} }
@Test @Test

@ -68,6 +68,7 @@ class TopicViewModelTest {
topicsRepository = topicsRepository, topicsRepository = topicsRepository,
getSaveableNewsResourcesStream = getSaveableNewsResourcesStreamUseCase getSaveableNewsResourcesStream = getSaveableNewsResourcesStreamUseCase
) )
newsRepository.sendNewsResources(emptyList())
} }
@Test @Test

Loading…
Cancel
Save