diff --git a/app/src/main/java/dev/msfjarvis/lobsters/data/remote/LobstersPagingSource.kt b/app/src/main/java/dev/msfjarvis/lobsters/data/remote/LobstersPagingSource.kt index 851bb167..24b266ed 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/data/remote/LobstersPagingSource.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/data/remote/LobstersPagingSource.kt @@ -14,9 +14,17 @@ class LobstersPagingSource @Inject constructor( override suspend fun load(params: LoadParams): LoadResult { return try { val page = params.key ?: 1 - val savedPosts = lobstersRepository.getCachedPosts() val posts = lobstersApi.getHottestPosts(page).map { post -> - post.copy(is_saved = savedPosts.contains(post.short_id)) + // We can replace these two lines below with a getOrInsertPost repository method. + val dbPost = lobstersRepository.getPost(post.short_id) + return@map if (dbPost == null) { + // If db does not contain the post, add it and return the original post + lobstersRepository.addPostToDB(post) + post + } else { + // Otherwise return the db post + dbPost + } } LoadResult.Page( diff --git a/app/src/main/java/dev/msfjarvis/lobsters/data/repo/LobstersRepository.kt b/app/src/main/java/dev/msfjarvis/lobsters/data/repo/LobstersRepository.kt index 28c70634..c42279b1 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/data/repo/LobstersRepository.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/data/repo/LobstersRepository.kt @@ -8,32 +8,33 @@ import javax.inject.Inject class LobstersRepository @Inject constructor(private val lobstersDatabase: LobstersDatabase) { - private var savedPostCache: MutableSet? = null - fun isPostSaved(postId: String): Boolean { - savedPostCache ?: return false - return requireNotNull(savedPostCache).contains(postId) + // returns the post if it exists and its is_saved property is true + val post = lobstersDatabase.postQueries.isPostSaved(postId).executeAsOneOrNull() + return post != null + } + + suspend fun getPost(postId: String): LobstersPost? = withContext(Dispatchers.IO) { + return@withContext lobstersDatabase.postQueries.selectPost(postId).executeAsOneOrNull() + } + + suspend fun getSavedPosts(): List = withContext(Dispatchers.IO) { + return@withContext lobstersDatabase.postQueries.selectSavedPosts().executeAsList() + } + + suspend fun addPostToDB(post: LobstersPost) = withContext(Dispatchers.IO) { + lobstersDatabase.postQueries.insertOrReplacePost(post) } suspend fun savePost(post: LobstersPost) = withContext(Dispatchers.IO) { - val isElementAdded = getCachedPosts().add(post.short_id) - if (isElementAdded) { + if (!isPostSaved(post.short_id)) { lobstersDatabase.postQueries.savePost(post.short_id) } } suspend fun removeSavedPost(post: LobstersPost) = withContext(Dispatchers.IO) { - val isElementRemoved = getCachedPosts().remove(post.short_id) - if (isElementRemoved) { + if (isPostSaved(post.short_id)) { lobstersDatabase.postQueries.removeSavedPost(post.short_id) } } - - suspend fun getCachedPosts(): MutableSet = 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) - } } diff --git a/app/src/main/java/dev/msfjarvis/lobsters/injection/DatabaseModule.kt b/app/src/main/java/dev/msfjarvis/lobsters/injection/DatabaseModule.kt index bd22e251..ca703031 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/injection/DatabaseModule.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/injection/DatabaseModule.kt @@ -12,17 +12,20 @@ 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 +import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object DatabaseModule { @Provides + @Singleton fun providesSqlDriver(@ApplicationContext context: Context): SqlDriver { return AndroidSqliteDriver(LobstersDatabase.Schema, context) } @Provides + @Singleton fun providesLobstersDatabase(sqlDriver: SqlDriver): LobstersDatabase { return LobstersDatabase(sqlDriver, LobstersPost.Adapter(SubmitterAdapter(), TagsAdapter())) } diff --git a/app/src/main/java/dev/msfjarvis/lobsters/ui/main/MainActivity.kt b/app/src/main/java/dev/msfjarvis/lobsters/ui/main/MainActivity.kt index ec54d98f..ade5f6f2 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/ui/main/MainActivity.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/ui/main/MainActivity.kt @@ -64,6 +64,8 @@ fun LobstersApp() { val savedPosts by viewModel.savedPosts.collectAsState() val hottestPostsListState = rememberLazyListState() + viewModel.getSavedPosts() + Scaffold( bottomBar = { LobstersBottomNav( diff --git a/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt b/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt index 85a84c8d..d4182e11 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt @@ -14,6 +14,10 @@ import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource @@ -65,6 +69,8 @@ fun LobstersItem( onLongClick: () -> Unit, onSaveButtonClick: () -> Unit, ) { + var isSaved by remember { mutableStateOf(post.is_saved ?: false) } + Surface( modifier = Modifier .fillMaxWidth() @@ -121,11 +127,14 @@ fun LobstersItem( }, ) IconResource( - resourceId = if (post.is_saved == true) R.drawable.ic_favorite_24px else R.drawable.ic_favorite_border_24px, + resourceId = if (isSaved) R.drawable.ic_favorite_24px else R.drawable.ic_favorite_border_24px, modifier = Modifier .padding(8.dp) .clickable( - onClick = onSaveButtonClick, + onClick = { + onSaveButtonClick() + isSaved = isSaved.not() + }, indication = rememberRipple(), ) .constrainAs(saveButton) { diff --git a/app/src/main/java/dev/msfjarvis/lobsters/ui/viewmodel/LobstersViewModel.kt b/app/src/main/java/dev/msfjarvis/lobsters/ui/viewmodel/LobstersViewModel.kt index 521f91a2..8b26eae5 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/ui/viewmodel/LobstersViewModel.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/ui/viewmodel/LobstersViewModel.kt @@ -20,7 +20,7 @@ class LobstersViewModel @Inject constructor( ) : ViewModel() { private val _savedPosts = MutableStateFlow>(emptyList()) val savedPosts = _savedPosts.asStateFlow() - val posts = Pager(PagingConfig(25)) { + val posts = Pager(PagingConfig(5)) { pagingSource }.flow @@ -31,17 +31,25 @@ class LobstersViewModel @Inject constructor( } } + fun getSavedPosts() { + viewModelScope.launch { + lobstersRepository.getSavedPosts().forEach { + _savedPosts.value = _savedPosts.value + it + } + } + } + private fun savePost(post: LobstersPost) { viewModelScope.launch { lobstersRepository.savePost(post) - _savedPosts.value = _savedPosts.value + post + _savedPosts.value = _savedPosts.value + post.copy(is_saved = true) } } private fun removeSavedPost(post: LobstersPost) { viewModelScope.launch { lobstersRepository.removeSavedPost(post) - _savedPosts.value = _savedPosts.value - post + _savedPosts.value = _savedPosts.value - post.copy(is_saved = true) } } } diff --git a/database/src/main/sqldelight/dev/msfjarvis/lobsters/data/local/Post.sq b/database/src/main/sqldelight/dev/msfjarvis/lobsters/data/local/Post.sq index 6e9577ca..a8aade62 100644 --- a/database/src/main/sqldelight/dev/msfjarvis/lobsters/data/local/Post.sq +++ b/database/src/main/sqldelight/dev/msfjarvis/lobsters/data/local/Post.sq @@ -56,6 +56,12 @@ deleteAllPosts: DELETE FROM LobstersPost; +isPostSaved: +SELECT * +FROM LobstersPost +WHERE short_id = ? +AND is_saved = 1; + selectCount: SELECT COUNT(*) FROM LobstersPost;