mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-17 22:37:03 +05:30
Add LobstersRepo to handle DB operations
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
This commit is contained in:
parent
4f9d0c09ef
commit
3bcdcbdac7
8 changed files with 99 additions and 7 deletions
|
@ -44,6 +44,7 @@ dependencies {
|
||||||
implementation(Dependencies.Kotlin.Coroutines.android)
|
implementation(Dependencies.Kotlin.Coroutines.android)
|
||||||
implementation(Dependencies.ThirdParty.accompanist)
|
implementation(Dependencies.ThirdParty.accompanist)
|
||||||
implementation(Dependencies.ThirdParty.Moshi.lib)
|
implementation(Dependencies.ThirdParty.Moshi.lib)
|
||||||
|
implementation(Dependencies.ThirdParty.SQLDelight.androidDriver)
|
||||||
testImplementation(Dependencies.Testing.junit)
|
testImplementation(Dependencies.Testing.junit)
|
||||||
androidTestImplementation(Dependencies.Testing.daggerHilt)
|
androidTestImplementation(Dependencies.Testing.daggerHilt)
|
||||||
androidTestImplementation(Dependencies.Testing.uiTest)
|
androidTestImplementation(Dependencies.Testing.uiTest)
|
||||||
|
|
|
@ -3,16 +3,21 @@ 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.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 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)
|
val savedPosts = lobstersRepository.getCachedPosts()
|
||||||
|
val posts = lobstersApi.getHottestPosts(page).map { post ->
|
||||||
|
post.copy(is_saved = savedPosts.contains(post.short_id))
|
||||||
|
}
|
||||||
|
|
||||||
LoadResult.Page(
|
LoadResult.Page(
|
||||||
data = posts,
|
data = posts,
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package dev.msfjarvis.lobsters.data.repo
|
||||||
|
|
||||||
|
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
||||||
|
import dev.msfjarvis.lobsters.database.LobstersDatabase
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LobstersRepository @Inject constructor(private val lobstersDatabase: LobstersDatabase) {
|
||||||
|
|
||||||
|
private var savedPostCache: MutableSet<String>? = null
|
||||||
|
|
||||||
|
fun isPostSaved(postId: String): Boolean {
|
||||||
|
savedPostCache ?: return false
|
||||||
|
return requireNotNull(savedPostCache).contains(postId)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun savePost(post: LobstersPost) = withContext(Dispatchers.IO) {
|
||||||
|
val isElementAdded = getCachedPosts().add(post.short_id)
|
||||||
|
if (isElementAdded) {
|
||||||
|
lobstersDatabase.postQueries.savePost(post.short_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun removeSavedPost(post: LobstersPost) = withContext(Dispatchers.IO) {
|
||||||
|
val isElementRemoved = getCachedPosts().remove(post.short_id)
|
||||||
|
if (isElementRemoved) {
|
||||||
|
lobstersDatabase.postQueries.removeSavedPost(post.short_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getCachedPosts(): MutableSet<String> = withContext(Dispatchers.IO) {
|
||||||
|
if (savedPostCache != null) return@withContext requireNotNull(savedPostCache)
|
||||||
|
|
||||||
|
val dbPosts = lobstersDatabase.postQueries.selectSavedPosts().executeAsList()
|
||||||
|
savedPostCache = dbPosts.filter { it.is_saved ?: false }.map { it.short_id }.toMutableSet()
|
||||||
|
return@withContext requireNotNull(savedPostCache)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package dev.msfjarvis.lobsters.injection
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.squareup.sqldelight.android.AndroidSqliteDriver
|
||||||
|
import com.squareup.sqldelight.db.SqlDriver
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
||||||
|
import dev.msfjarvis.lobsters.database.LobstersDatabase
|
||||||
|
import dev.msfjarvis.lobsters.model.SubmitterAdapter
|
||||||
|
import dev.msfjarvis.lobsters.model.TagsAdapter
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
object DatabaseModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesSqlDriver(@ApplicationContext context: Context): SqlDriver {
|
||||||
|
return AndroidSqliteDriver(LobstersDatabase.Schema, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesLobstersDatabase(sqlDriver: SqlDriver): LobstersDatabase {
|
||||||
|
return LobstersDatabase(sqlDriver, LobstersPost.Adapter(SubmitterAdapter(), TagsAdapter()))
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,14 +78,14 @@ fun LobstersApp() {
|
||||||
HottestPosts(
|
HottestPosts(
|
||||||
posts = hottestPosts,
|
posts = hottestPosts,
|
||||||
listState = hottestPostsListState,
|
listState = hottestPostsListState,
|
||||||
saveAction = viewModel::savePost,
|
saveAction = viewModel::toggleSave,
|
||||||
modifier = Modifier.padding(bottom = innerPadding.bottom),
|
modifier = Modifier.padding(bottom = innerPadding.bottom),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(Destination.Saved.route) {
|
composable(Destination.Saved.route) {
|
||||||
SavedPosts(
|
SavedPosts(
|
||||||
posts = savedPosts,
|
posts = savedPosts,
|
||||||
saveAction = viewModel::removeSavedPost,
|
saveAction = viewModel::toggleSave,
|
||||||
modifier = Modifier.padding(bottom = innerPadding.bottom),
|
modifier = Modifier.padding(bottom = innerPadding.bottom),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ fun LobstersItem(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
IconResource(
|
IconResource(
|
||||||
resourceId = R.drawable.ic_favorite_border_24px,
|
resourceId = if (post.is_saved == true) R.drawable.ic_favorite_24px else R.drawable.ic_favorite_border_24px,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.clickable(
|
.clickable(
|
||||||
|
|
|
@ -5,8 +5,9 @@ import androidx.lifecycle.viewModelScope
|
||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.msfjarvis.lobsters.data.remote.LobstersPagingSource
|
|
||||||
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
import dev.msfjarvis.lobsters.data.local.LobstersPost
|
||||||
|
import dev.msfjarvis.lobsters.data.remote.LobstersPagingSource
|
||||||
|
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.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -15,6 +16,7 @@ import javax.inject.Inject
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LobstersViewModel @Inject constructor(
|
class LobstersViewModel @Inject constructor(
|
||||||
private val pagingSource: LobstersPagingSource,
|
private val pagingSource: LobstersPagingSource,
|
||||||
|
private val lobstersRepository: LobstersRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _savedPosts = MutableStateFlow<List<LobstersPost>>(emptyList())
|
private val _savedPosts = MutableStateFlow<List<LobstersPost>>(emptyList())
|
||||||
val savedPosts = _savedPosts.asStateFlow()
|
val savedPosts = _savedPosts.asStateFlow()
|
||||||
|
@ -22,13 +24,24 @@ class LobstersViewModel @Inject constructor(
|
||||||
pagingSource
|
pagingSource
|
||||||
}.flow
|
}.flow
|
||||||
|
|
||||||
fun savePost(post: LobstersPost) {
|
fun toggleSave(post: LobstersPost) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val isSaved = lobstersRepository.isPostSaved(post.short_id)
|
||||||
|
if (isSaved) removeSavedPost(post) else savePost(post)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeSavedPost(post: LobstersPost) {
|
private fun savePost(post: LobstersPost) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
lobstersRepository.savePost(post)
|
||||||
|
_savedPosts.value = _savedPosts.value + post
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeSavedPost(post: LobstersPost) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
lobstersRepository.removeSavedPost(post)
|
||||||
|
_savedPosts.value = _savedPosts.value - post
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,11 @@ selectAllPosts:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM LobstersPost;
|
FROM LobstersPost;
|
||||||
|
|
||||||
|
selectSavedPosts:
|
||||||
|
SELECT *
|
||||||
|
FROM LobstersPost
|
||||||
|
WHERE is_saved = 1;
|
||||||
|
|
||||||
selectPost:
|
selectPost:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM LobstersPost
|
FROM LobstersPost
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue