Skip to content

Commit

Permalink
Cleaned up
Browse files Browse the repository at this point in the history
  • Loading branch information
nsmirosh committed Oct 15, 2024
1 parent 305c5ac commit 095d5dc
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package nick.mirosh.newsapp.data.repository

import nick.mirosh.newsapp.data.database.ArticleDao
import nick.mirosh.newsapp.domain.ErrorType
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.repository.NewsRepository
import nick.mirosh.newsapp.domain.mapper.news.DTOtoDatabaseArticleMapper
import nick.mirosh.newsapp.domain.mapper.news.DatabaseToDomainArticleMapper
Expand All @@ -21,43 +21,43 @@ class NewsRepositoryImpl @Inject constructor(
private val databaseToDomainArticleMapper: DatabaseToDomainArticleMapper,
private val dtoToDatabaseArticleMapper: DTOtoDatabaseArticleMapper,
) : NewsRepository {
override suspend fun getNewsArticles(): Resource<List<Article>> {
override suspend fun getNewsArticles(): Result<List<Article>> {
return try {
newsRemoteDataSource?.getHeadlines()?.let {
newsLocalDataSource.insertAll(dtoToDatabaseArticleMapper.map(it))
}
Resource.Success(
Result.Success(
databaseToDomainArticleMapper.map(getAllArticlesFromDb())
)
} catch (e: Exception) {
e.logStackTrace(tag)
Resource.Error(ErrorType.General)
Result.Error(ErrorType.General)
}
}


override suspend fun getFavoriteArticles() =
try {
Resource.Success(
Result.Success(
databaseToDomainArticleMapper.map(
newsLocalDataSource.getLikedArticles()
)
)
} catch (e: Exception) {
e.logStackTrace(tag)
Resource.Error(ErrorType.General)
Result.Error(ErrorType.General)
}

override suspend fun updateArticle(article: Article) =
try {
val updatedRowId = newsLocalDataSource.insert(article.asDatabaseModel())
if (updatedRowId != -1L)
Resource.Success(article)
Result.Success(article)
else
Resource.Error(ErrorType.General)
Result.Error(ErrorType.General)
} catch (e: Exception) {
e.logStackTrace(tag)
Resource.Error(ErrorType.General)
Result.Error(ErrorType.General)
}

private suspend fun getAllArticlesFromDb() =
Expand Down
23 changes: 0 additions & 23 deletions app/src/main/java/nick/mirosh/newsapp/domain/Resource.kt

This file was deleted.

21 changes: 21 additions & 0 deletions app/src/main/java/nick/mirosh/newsapp/domain/Result.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package nick.mirosh.newsapp.domain

/**
* Represents a result that can either contain successful data or an error.
*
*/
sealed class Result<out T> {
/**
* Represents successful data.
*
* @property data Data of generic type T.
*/
data class Success<out T>(val data: T) : Result<T>()

/**
* Represents a failure.
*
* @property error The type of error, represented by ErrorType.
*/
data class Error(val error: ErrorType) : Result<Nothing>()
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package nick.mirosh.newsapp.domain.feed.repository

import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.model.Article

interface NewsRepository {
suspend fun getNewsArticles(): Resource<List<Article>>
suspend fun getFavoriteArticles(): Resource<List<Article>>
suspend fun updateArticle(article: Article): Resource<Article>
suspend fun getNewsArticles(): Result<List<Article>>
suspend fun getFavoriteArticles(): Result<List<Article>>
suspend fun updateArticle(article: Article): Result<Article>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import nick.mirosh.newsapp.di.IoDispatcher
import nick.mirosh.newsapp.di.Universal
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.model.Article
import nick.mirosh.newsapp.domain.feed.repository.NewsRepository
import javax.inject.Inject
Expand All @@ -14,7 +14,7 @@ class FetchArticlesUsecase @Inject constructor(
@IoDispatcher private val coroutineDispatcher: CoroutineDispatcher,
) {

suspend operator fun invoke(): Resource<List<Article>> {
suspend operator fun invoke(): Result<List<Article>> {
return withContext(coroutineDispatcher) {
repository.getNewsArticles()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import nick.mirosh.newsapp.di.Cache
import nick.mirosh.newsapp.di.IoDispatcher
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.model.Article
import nick.mirosh.newsapp.domain.feed.repository.NewsRepository
import javax.inject.Inject
Expand All @@ -14,7 +14,7 @@ class LikeArticleUsecase @Inject constructor(
@IoDispatcher private val coroutineDispatcher: CoroutineDispatcher,
) {

suspend operator fun invoke(article: Article): Resource<Article> {
suspend operator fun invoke(article: Article): Result<Article> {
return withContext(coroutineDispatcher) {
repository.updateArticle(article.copy(liked = !article.liked))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import nick.mirosh.newsapp.di.Cache
import nick.mirosh.newsapp.di.IoDispatcher
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.model.Article
import nick.mirosh.newsapp.domain.feed.repository.NewsRepository
import javax.inject.Inject
Expand All @@ -14,7 +14,7 @@ class FetchFavoriteArticlesUsecase @Inject constructor(
@IoDispatcher private val coroutineDispatcher: CoroutineDispatcher,
) {

suspend operator fun invoke(): Resource<List<Article>> {
suspend operator fun invoke(): Result<List<Article>> {
return withContext(coroutineDispatcher) {
repository.getFavoriteArticles()
}
Expand Down
10 changes: 5 additions & 5 deletions app/src/main/java/nick/mirosh/newsapp/ui/FeedViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.model.Article
import nick.mirosh.newsapp.domain.feed.usecase.FetchArticlesUsecase
import nick.mirosh.newsapp.domain.feed.usecase.LikeArticleUsecase
Expand Down Expand Up @@ -44,26 +44,26 @@ class FeedViewModel @Inject constructor(
private suspend fun fetchArticles() {
_uiState.value = FeedUIState.Loading
_uiState.value = when (val result = fetchArticlesUsecase()) {
is Resource.Success -> {
is Result.Success -> {
_articles.clear()
_articles.addAll(result.data)
FeedUIState.Feed(articles)
}

is Resource.Error -> FeedUIState.Failed
is Result.Error -> FeedUIState.Failed
}
}

//https://stackoverflow.com/questions/74699081/jetpack-compose-lazy-column-all-items-recomposes-when-a-single-item-update
fun onLikeClick(article: Article) {
viewModelScope.launch {
when (val result = likeArticleUsecase(article)) {
is Resource.Success -> {
is Result.Success -> {
val index = articles.indexOfFirst { it.url == result.data.url }
_articles[index] = result.data
}

is Resource.Error -> {
is Result.Error -> {
MyLogger.e("MainViewModel", "Error: ${result.error}")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.usecase.articles.FetchFavoriteArticlesUsecase
import nick.mirosh.newsapp.utils.MyLogger
import javax.inject.Inject
Expand All @@ -22,13 +22,13 @@ class FavoriteArticlesViewModel @Inject constructor(
init {
viewModelScope.launch {
when (val result = fetchFavoriteArticlesUsecase()) {
is Resource.Success ->
is nick.mirosh.newsapp.domain.Resource.Result.Success ->
_uiState.value = if (result.data.isEmpty())
FavoriteArticlesUIState.FavoriteArticlesEmpty
else
FavoriteArticlesUIState.FavoriteArticles(result.data)

is Resource.Error -> {
is nick.mirosh.newsapp.domain.Resource.Result.Error -> {
_uiState.value = FavoriteArticlesUIState.Failed
MyLogger.e("FavoriteArticlesViewModel", "Error = ${result.error}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.runTest
import nick.mirosh.newsapp.domain.ErrorType
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.model.Article
import nick.mirosh.newsapp.domain.usecase.articles.FetchFavoriteArticlesUsecase
import nick.mirosh.newsapp.helpers.MainDispatcherRule
Expand Down Expand Up @@ -35,7 +35,7 @@ class FavoriteArticlesViewModelTest {
fun init_withEmptyArticleResponse_sends_FavoriteArticlesEmptyEvent_toTheUI() = runTest {

//Arrange
val result = Resource.Success(listOf<Article>())
val result = Result.Success(listOf<Article>())
`when`(fetchFavoriteArticlesUsecase.invoke()).thenReturn(result)

//Act
Expand All @@ -52,7 +52,7 @@ class FavoriteArticlesViewModelTest {
fun init_withEmptyArticleResponse_sends_FavoriteArticlesEvent_toTheUI() = runTest {

//Arrange
val result = Resource.Success(listOf(likedArticle))
val result = Result.Success(listOf(likedArticle))
`when`(fetchFavoriteArticlesUsecase.invoke()).thenReturn(result)

//Act
Expand All @@ -69,7 +69,7 @@ class FavoriteArticlesViewModelTest {
fun init_withEmptyArticleResponse_sends_Error_toTheUI() = runTest {

//Arrange
val result = Resource.Error(ErrorType.General)
val result = Result.Error(ErrorType.General)
`when`(fetchFavoriteArticlesUsecase.invoke()).thenReturn(result)

//Act
Expand Down
20 changes: 7 additions & 13 deletions app/src/test/java/nick/mirosh/newsapp/FeedViewModelTest.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
package nick.mirosh.newsapp

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import nick.mirosh.newsapp.domain.ErrorType
import nick.mirosh.newsapp.domain.Resource
import nick.mirosh.newsapp.domain.Result
import nick.mirosh.newsapp.domain.feed.model.Article
import nick.mirosh.newsapp.domain.feed.usecase.FetchArticlesUsecase
import nick.mirosh.newsapp.domain.feed.usecase.LikeArticleUsecase
Expand Down Expand Up @@ -58,7 +52,7 @@ class FeedViewModelTest {
) {
//Arrange
val articles = listOf<Article>()
val result = Resource.Success(articles)
val result = Result.Success(articles)
`when`(fetchArticlesUsecase.invoke()).thenReturn(result)
`when`(networkConnectivityUseCase.invoke()).thenReturn(flowOf(true))

Expand All @@ -77,7 +71,7 @@ class FeedViewModelTest {
@Test
fun init_getsTheArticleListWithFailure_sendsFailedEventToUI() = runTest {
//Arrange
val result = Resource.Error(ErrorType.General)
val result = Result.Error(ErrorType.General)
`when`(fetchArticlesUsecase.invoke()).thenReturn(result)
`when`(networkConnectivityUseCase.invoke()).thenReturn(flowOf ( true ))
val expected = FeedUIState.Failed
Expand All @@ -97,11 +91,11 @@ class FeedViewModelTest {
@Test
fun onLikeClick_withValidArticle_updatesArticlesList() = runTest {
//Arrange
val fetchArticlesResult = Resource.Success(listOf(notLikedArticle))
val fetchArticlesResult = Result.Success(listOf(notLikedArticle))
`when`(fetchArticlesUsecase.invoke()).thenReturn(fetchArticlesResult)
`when`(networkConnectivityUseCase.invoke()).thenReturn(flow { emit(true) })

val result = Resource.Success(likedArticle)
val result = Result.Success(likedArticle)
`when`(likeArticleUsecase.invoke(notLikedArticle)).thenReturn(result)
val expected = listOf(likedArticle)

Expand All @@ -125,11 +119,11 @@ class FeedViewModelTest {
@Test
fun onLikeClick_withFailure_doesNotUpdateArticleList() = runTest {
//Arrange
val fetchArticlesResult = Resource.Success(listOf(notLikedArticle))
val fetchArticlesResult = Result.Success(listOf(notLikedArticle))
`when`(fetchArticlesUsecase.invoke()).thenReturn(fetchArticlesResult)
`when`(networkConnectivityUseCase.invoke()).thenReturn(flow { emit(true) })

val result = Resource.Error(ErrorType.General)
val result = Result.Error(ErrorType.General)
`when`(likeArticleUsecase.invoke(notLikedArticle)).thenReturn(result)
val expected = listOf(notLikedArticle)

Expand Down

0 comments on commit 095d5dc

Please sign in to comment.