mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-18 04:27:02 +05:30
Merge #101
101: Fix cache issues r=msfjarvis a=Skrilltrax Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
This commit is contained in:
commit
147f2d23a5
5 changed files with 66 additions and 20 deletions
|
@ -5,7 +5,7 @@ import dagger.Lazy
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.components.ViewModelComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import dev.msfjarvis.lobsters.data.api.LobstersApi
|
import dev.msfjarvis.lobsters.data.api.LobstersApi
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
|
@ -13,7 +13,7 @@ import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
import retrofit2.create
|
import retrofit2.create
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(ViewModelComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
object ApiModule {
|
object ApiModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
package dev.msfjarvis.lobsters.data.remote
|
package dev.msfjarvis.lobsters.data.remote
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import dev.msfjarvis.lobsters.data.api.LobstersApi
|
|
||||||
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
||||||
|
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LobstersPagingSource @Inject constructor(
|
class LobstersPagingSource @Inject constructor(
|
||||||
private val lobstersApi: LobstersApi,
|
private val lobstersRepository: LobstersRepository,
|
||||||
) : PagingSource<Int, LobstersPost>() {
|
) : PagingSource<Int, LobstersPost>() {
|
||||||
|
|
||||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, LobstersPost> {
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, LobstersPost> {
|
||||||
return try {
|
return try {
|
||||||
val page = params.key ?: 1
|
val page = params.key ?: 1
|
||||||
val posts = lobstersApi.getHottestPosts(page)
|
// Update cache before fetching a list.
|
||||||
|
// This is done to make sure that we can update the isSaved status of incoming posts.
|
||||||
|
lobstersRepository.updateCache()
|
||||||
|
val posts = lobstersRepository.fetchPosts(page)
|
||||||
|
|
||||||
LoadResult.Page(
|
LoadResult.Page(
|
||||||
data = posts,
|
data = posts,
|
||||||
|
|
|
@ -1,26 +1,21 @@
|
||||||
package dev.msfjarvis.lobsters.data.repo
|
package dev.msfjarvis.lobsters.data.repo
|
||||||
|
|
||||||
|
import dev.msfjarvis.lobsters.data.api.LobstersApi
|
||||||
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
||||||
import dev.msfjarvis.lobsters.database.LobstersDatabase
|
import dev.msfjarvis.lobsters.database.LobstersDatabase
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class LobstersRepository @Inject constructor(private val lobstersDatabase: LobstersDatabase) {
|
class LobstersRepository constructor(
|
||||||
|
private val lobstersApi: LobstersApi,
|
||||||
|
private val lobstersDatabase: LobstersDatabase,
|
||||||
|
) {
|
||||||
|
|
||||||
private val savedPostsCache: MutableMap<String, LobstersPost> = mutableMapOf()
|
private val savedPostsCache: MutableMap<String, LobstersPost> = mutableMapOf()
|
||||||
private val coroutineScope = CoroutineScope(Job() + Dispatchers.IO)
|
private val _isCacheReady = MutableStateFlow(false)
|
||||||
|
val isCacheReady = _isCacheReady.asStateFlow()
|
||||||
init {
|
|
||||||
coroutineScope.launch {
|
|
||||||
getAllPosts().forEach {
|
|
||||||
savedPostsCache.putIfAbsent(it.short_id, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isPostSaved(postId: String): Boolean {
|
fun isPostSaved(postId: String): Boolean {
|
||||||
return savedPostsCache.containsKey(postId)
|
return savedPostsCache.containsKey(postId)
|
||||||
|
@ -34,6 +29,20 @@ class LobstersRepository @Inject constructor(private val lobstersDatabase: Lobst
|
||||||
return savedPostsCache.values.toList()
|
return savedPostsCache.values.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun fetchPosts(page: Int): List<LobstersPost> = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext lobstersApi.getHottestPosts(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateCache() {
|
||||||
|
if (_isCacheReady.value) return
|
||||||
|
val posts = getAllPosts()
|
||||||
|
|
||||||
|
posts.forEach {
|
||||||
|
savedPostsCache[it.short_id] = it
|
||||||
|
}
|
||||||
|
_isCacheReady.value = true
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun getPost(postId: String): LobstersPost? = withContext(Dispatchers.IO) {
|
private suspend fun getPost(postId: String): LobstersPost? = withContext(Dispatchers.IO) {
|
||||||
return@withContext lobstersDatabase.postQueries.selectPost(postId).executeAsOneOrNull()
|
return@withContext lobstersDatabase.postQueries.selectPost(postId).executeAsOneOrNull()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package dev.msfjarvis.lobsters.injection
|
||||||
|
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import dev.msfjarvis.lobsters.data.api.LobstersApi
|
||||||
|
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
|
||||||
|
import dev.msfjarvis.lobsters.database.LobstersDatabase
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
object RepositoryModule {
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideLobstersRepository(
|
||||||
|
lobstersApi: LobstersApi,
|
||||||
|
lobstersDatabase: LobstersDatabase
|
||||||
|
): LobstersRepository {
|
||||||
|
return LobstersRepository(lobstersApi, lobstersDatabase)
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,8 @@ import dev.msfjarvis.lobsters.data.remote.LobstersPagingSource
|
||||||
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
|
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -19,12 +21,20 @@ class LobstersViewModel @Inject constructor(
|
||||||
private val lobstersRepository: LobstersRepository,
|
private val lobstersRepository: LobstersRepository,
|
||||||
private val pagingSource: LobstersPagingSource,
|
private val pagingSource: LobstersPagingSource,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _savedPosts = MutableStateFlow(lobstersRepository.getAllPostsFromCache())
|
private val _savedPosts = MutableStateFlow<List<LobstersPost>>(emptyList())
|
||||||
val savedPosts = _savedPosts.asStateFlow()
|
val savedPosts = _savedPosts.asStateFlow()
|
||||||
val posts = Pager(PagingConfig(25)) {
|
val posts = Pager(PagingConfig(25)) {
|
||||||
pagingSource
|
pagingSource
|
||||||
}.flow.cachedIn(viewModelScope)
|
}.flow.cachedIn(viewModelScope)
|
||||||
|
|
||||||
|
init {
|
||||||
|
lobstersRepository.isCacheReady.onEach { ready ->
|
||||||
|
if (ready) {
|
||||||
|
_savedPosts.value = lobstersRepository.getAllPostsFromCache()
|
||||||
|
}
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
fun toggleSave(post: LobstersPost) {
|
fun toggleSave(post: LobstersPost) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val isSaved = lobstersRepository.isPostSaved(post.short_id)
|
val isSaved = lobstersRepository.isPostSaved(post.short_id)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue