Skip to content

Commit

Permalink
Implement curated photos cache
Browse files Browse the repository at this point in the history
  • Loading branch information
SIKV committed Jun 28, 2024
1 parent 8949b18 commit 8516b29
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 46 deletions.
6 changes: 6 additions & 0 deletions data/src/main/java/com/github/sikv/photos/data/Result.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.github.sikv.photos.data

sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.github.sikv.photos.data.repository

import com.github.sikv.photos.data.Result
import com.github.sikv.photos.domain.PhotoSource
import com.github.sikv.photos.domain.Photo

interface PhotosRepository {
suspend fun getPhoto(id: String, source: PhotoSource): Photo?
suspend fun getCuratedPhotos(page: Int, perPage: Int): List<Photo>
suspend fun getCuratedPhotos(page: Int, perPage: Int): Result<List<Photo>>
suspend fun searchPhotos(query: String, page: Int, perPage: Int, source: PhotoSource): List<Photo>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.sikv.photos.data.repository.impl

import com.github.sikv.photos.api.client.ApiClient
import com.github.sikv.photos.data.Result
import com.github.sikv.photos.data.repository.FavoritesRepository
import com.github.sikv.photos.data.repository.PhotosRepository
import com.github.sikv.photos.domain.Photo
Expand All @@ -23,13 +24,30 @@ class PhotosRepositoryImpl @Inject constructor(
}
}

override suspend fun getCuratedPhotos(page: Int, perPage: Int): List<Photo> {
return apiClient.pexelsClient
.getCuratedPhotos(page + getPageNumberComplement(PhotoSource.PEXELS), perPage)
.photos
.onEach { photo ->
favoritesRepository.populateFavorite(photo)
/**
* Uses Network First (stale-if-error) caching strategy (only for page 0).
*/
override suspend fun getCuratedPhotos(page: Int, perPage: Int): Result<List<Photo>> {
try {
val photos = apiClient.pexelsClient
.getCuratedPhotos(page + getPageNumberComplement(PhotoSource.PEXELS), perPage)
.photos
.onEach { photo ->
favoritesRepository.populateFavorite(photo)
}

if (page == 0) {
// TODO: Store in cache.
}
return Result.Success(photos)
} catch (e: Exception) {
if (page == 0) {
// TODO: Read cache and return result.
return Result.Error(e)
} else {
return Result.Error(e)
}
}
}

override suspend fun searchPhotos(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.github.sikv.photos.curated

import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.github.sikv.photos.data.Result
import com.github.sikv.photos.data.repository.PhotosRepository
import com.github.sikv.photos.domain.Photo

Expand All @@ -13,16 +14,20 @@ internal class CuratedPhotosPagingSource(
val initialPosition = 0
val position = params.key ?: initialPosition

return try {
val photos = photosRepository.getCuratedPhotos(position, params.loadSize)
val photosResult = photosRepository.getCuratedPhotos(position, params.loadSize)

LoadResult.Page(
data = photos,
prevKey = if (position == initialPosition) null else position - 1,
nextKey = if (photos.isEmpty()) null else position + 1
)
} catch (e: Exception) {
return LoadResult.Error(e)
return when (photosResult) {
is Result.Success -> {
LoadResult.Page(
data = photosResult.data,
prevKey = if (position == initialPosition) null else position - 1,
nextKey = if (photosResult.data.isEmpty()) null else position + 1
)
}

is Result.Error -> {
LoadResult.Error(photosResult.exception)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState
import androidx.paging.compose.collectAsLazyPagingItems
import com.github.sikv.photos.compose.ui.DynamicPhotoItem
import com.github.sikv.photos.compose.ui.Scaffold
import com.github.sikv.photos.compose.ui.SwitchLayoutAction
import com.github.sikv.photos.domain.Photo

// TODO: Add loading indicator.

@Composable
internal fun CuratedPhotosScreen(
onPhotoClick: (Photo) -> Unit,
Expand All @@ -25,7 +24,9 @@ internal fun CuratedPhotosScreen(
onDownloadPhotoClick: (Photo) -> Unit,
viewModel: CuratedPhotosViewModel = hiltViewModel(),
) {
val photos = viewModel.getCuratedPhotos().collectAsLazyPagingItems()
val photos = viewModel.curatedPhotos.collectAsLazyPagingItems()
val loadState = photos.loadState.refresh

val listLayout by viewModel.listLayoutState.collectAsStateWithLifecycle()

Scaffold(
Expand All @@ -37,24 +38,37 @@ internal fun CuratedPhotosScreen(
)
}
) {
LazyVerticalGrid(
columns = GridCells.Fixed(listLayout.spanCount)
) {
items(photos.itemCount) { index ->
photos[index]?.let { photo ->
val isFavorite by viewModel.isFavorite(photo).collectAsStateWithLifecycle(initialValue = false)
when (loadState) {
is LoadState.Loading -> {
// TODO: Implement.
Text(text = "Loading...")
}
is LoadState.Error -> {
// TODO: Implement.
Text(text = "Error!")
}
is LoadState.NotLoading -> {
LazyVerticalGrid(
columns = GridCells.Fixed(listLayout.spanCount)
) {
items(photos.itemCount) { index ->
photos[index]?.let { photo ->
val isFavorite by viewModel.isFavorite(photo)
.collectAsStateWithLifecycle(initialValue = false)

DynamicPhotoItem(
photo = photo,
isFavorite = isFavorite,
listLayout = listLayout,
onPhotoClick = onPhotoClick,
onPhotoAttributionClick = onPhotoAttributionClick,
onPhotoActionsClick = onPhotoActionsClick,
onToggleFavoriteClick = viewModel::toggleFavorite,
onSharePhotoClick = onSharePhotoClick,
onDownloadPhotoClick = onDownloadPhotoClick
)
DynamicPhotoItem(
photo = photo,
isFavorite = isFavorite,
listLayout = listLayout,
onPhotoClick = onPhotoClick,
onPhotoAttributionClick = onPhotoAttributionClick,
onPhotoActionsClick = onPhotoActionsClick,
onToggleFavoriteClick = viewModel::toggleFavorite,
onSharePhotoClick = onSharePhotoClick,
onDownloadPhotoClick = onDownloadPhotoClick
)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.github.sikv.photos.common.PreferencesService
import com.github.sikv.photos.config.ConfigProvider
import com.github.sikv.photos.data.repository.FavoritesRepository2
Expand All @@ -27,6 +26,13 @@ internal class CuratedPhotosViewModel @Inject constructor(
private val configProvider: ConfigProvider
) : ViewModel() {

val curatedPhotos = Pager(
config = configProvider.getPagingConfig(),
pagingSourceFactory = {
CuratedPhotosPagingSource(photosRepository)
}
).flow

private val mutableListLayoutState = MutableStateFlow(preferencesService.getCuratedListLayout())
val listLayoutState: StateFlow<ListLayout> = mutableListLayoutState

Expand Down Expand Up @@ -55,15 +61,6 @@ internal class CuratedPhotosViewModel @Inject constructor(
}
}
}

fun getCuratedPhotos(): Flow<PagingData<Photo>> {
return Pager(
config = configProvider.getPagingConfig(),
pagingSourceFactory = {
CuratedPhotosPagingSource(photosRepository)
}
).flow
}
}

private fun ConfigProvider.getPagingConfig(): PagingConfig {
Expand Down

0 comments on commit 8516b29

Please sign in to comment.