101: Fix cache issues r=msfjarvis a=Skrilltrax



Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
This commit is contained in:
bors[bot] 2021-02-05 10:38:39 +00:00 committed by GitHub
commit 147f2d23a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 20 deletions

View file

@ -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

View file

@ -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,

View file

@ -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()
} }

View file

@ -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)
}
}

View file

@ -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)