android: integrate database

This commit is contained in:
Harsh Shandilya 2021-09-27 21:37:59 +05:30
parent de93f32b96
commit 86e659a180
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
6 changed files with 75 additions and 3 deletions

View file

@ -12,6 +12,7 @@ dependencies {
kapt(libs.dagger.hilt.compiler) kapt(libs.dagger.hilt.compiler)
implementation(projects.api) implementation(projects.api)
implementation(projects.common) implementation(projects.common)
implementation(projects.database)
implementation(libs.accompanist.insets) implementation(libs.accompanist.insets)
implementation(libs.accompanist.swiperefresh) implementation(libs.accompanist.swiperefresh)
implementation(libs.accompanist.sysuicontroller) implementation(libs.accompanist.sysuicontroller)
@ -21,6 +22,7 @@ dependencies {
implementation(libs.androidx.lifecycle.compose) implementation(libs.androidx.lifecycle.compose)
implementation(libs.androidx.paging.compose) implementation(libs.androidx.paging.compose)
implementation(libs.dagger.hilt.android) implementation(libs.dagger.hilt.android)
implementation(libs.sqldelight.extensions.coroutines)
implementation(libs.retrofit.moshiConverter) implementation(libs.retrofit.moshiConverter)
} }

View file

@ -15,6 +15,7 @@ import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
@ -35,6 +36,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import dev.msfjarvis.claw.android.viewmodel.ClawViewModel import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
import dev.msfjarvis.claw.common.theme.LobstersTheme import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
import kotlinx.coroutines.launch
private const val ScrollDelta = 50 private const val ScrollDelta = 50
@ -47,6 +49,7 @@ fun LobstersApp(
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
val scaffoldState = rememberScaffoldState() val scaffoldState = rememberScaffoldState()
val listState = rememberLazyListState() val listState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
var isFabVisible by remember { mutableStateOf(true) } var isFabVisible by remember { mutableStateOf(true) }
val nestedScrollConnection = remember { val nestedScrollConnection = remember {
object : NestedScrollConnection { object : NestedScrollConnection {
@ -97,6 +100,8 @@ fun LobstersApp(
items = items, items = items,
launchUrl = urlLauncher::launch, launchUrl = urlLauncher::launch,
listState = listState, listState = listState,
isSaved = viewModel::isPostSaved,
toggleSave = { coroutineScope.launch { viewModel.toggleSave(it) } },
modifier = Modifier.padding(top = 16.dp).nestedScroll(nestedScrollConnection), modifier = Modifier.padding(top = 16.dp).nestedScroll(nestedScrollConnection),
) )
} }

View file

@ -4,6 +4,11 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
@ -11,26 +16,34 @@ import androidx.paging.compose.items
import dev.msfjarvis.claw.android.ext.toDbModel import dev.msfjarvis.claw.android.ext.toDbModel
import dev.msfjarvis.claw.api.model.LobstersPost import dev.msfjarvis.claw.api.model.LobstersPost
import dev.msfjarvis.claw.common.posts.LobstersCard import dev.msfjarvis.claw.common.posts.LobstersCard
import dev.msfjarvis.claw.database.local.SavedPost
import kotlinx.coroutines.launch
@Composable @Composable
fun NetworkPosts( fun NetworkPosts(
items: LazyPagingItems<LobstersPost>, items: LazyPagingItems<LobstersPost>,
listState: LazyListState, listState: LazyListState,
launchUrl: (String) -> Unit, launchUrl: (String) -> Unit,
isSaved: suspend (SavedPost) -> Boolean,
toggleSave: (SavedPost) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val coroutineScope = rememberCoroutineScope()
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier.then(modifier), modifier = Modifier.then(modifier),
) { ) {
items(items) { item -> items(items) { item ->
if (item != null) { if (item != null) {
val dbModel = item.toDbModel()
var saved by remember(dbModel) { mutableStateOf(false) }
coroutineScope.launch { saved = isSaved(dbModel) }
LobstersCard( LobstersCard(
post = item.toDbModel(), post = dbModel,
isSaved = false, isSaved = saved,
viewPost = { launchUrl(item.url.ifEmpty { item.commentsUrl }) }, viewPost = { launchUrl(item.url.ifEmpty { item.commentsUrl }) },
viewComments = { launchUrl(item.commentsUrl) }, viewComments = { launchUrl(item.commentsUrl) },
toggleSave = {}, toggleSave = { toggleSave(dbModel) },
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp), modifier = Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp),
) )
} }

View file

@ -6,15 +6,24 @@ import androidx.paging.PagingConfig
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dev.msfjarvis.claw.android.paging.LobstersPagingSource import dev.msfjarvis.claw.android.paging.LobstersPagingSource
import dev.msfjarvis.claw.api.LobstersApi import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.database.local.SavedPost
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.mapLatest
@OptIn(ExperimentalCoroutinesApi::class)
@HiltViewModel @HiltViewModel
class ClawViewModel class ClawViewModel
@Inject @Inject
constructor( constructor(
api: LobstersApi, api: LobstersApi,
private val repository: SavedPostsRepository,
) : ViewModel() { ) : ViewModel() {
var lastPagingSource: LobstersPagingSource? = null var lastPagingSource: LobstersPagingSource? = null
private val savedPosts = flow { repository.savedPosts.collect { emit(it) } }
private val pager = private val pager =
Pager(PagingConfig(20)) { Pager(PagingConfig(20)) {
LobstersPagingSource(api::getHottestPosts).also { lastPagingSource = it } LobstersPagingSource(api::getHottestPosts).also { lastPagingSource = it }
@ -23,6 +32,20 @@ constructor(
val pagerFlow val pagerFlow
get() = pager.flow get() = pager.flow
suspend fun isPostSaved(post: SavedPost): Boolean {
return savedPosts.mapLatest { posts -> post in posts }.last()
}
suspend fun toggleSave(post: SavedPost) {
val saved = isPostSaved(post)
println("saved=$saved")
if (saved) {
repository.removePost(post)
} else {
repository.savePost(post)
}
}
fun reloadPosts() { fun reloadPosts() {
lastPagingSource?.invalidate() lastPagingSource?.invalidate()
} }

View file

@ -0,0 +1,28 @@
package dev.msfjarvis.claw.android.viewmodel
import com.squareup.sqldelight.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.local.SavedPost
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class SavedPostsRepository
@Inject
constructor(
database: LobstersDatabase,
) {
private val savedPostQueries = database.savedPostQueries
val savedPosts = savedPostQueries.selectAllPosts().asFlow().mapToList()
suspend fun savePost(post: SavedPost) {
println("Saving post: ${post.shortId}")
withContext(Dispatchers.IO) { savedPostQueries.insertOrReplacePost(post) }
}
suspend fun removePost(post: SavedPost) {
println("Removing post: ${post.shortId}")
withContext(Dispatchers.IO) { savedPostQueries.deletePost(post.shortId) }
}
}

View file

@ -43,5 +43,6 @@ retrofit-moshiConverter = { module = "com.squareup.retrofit2:converter-moshi", v
sqldelight-jvmDriver = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-jvmDriver = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
sqldelight-androidDriver = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-androidDriver = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-extensions-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" }
testing-mockWebServer = "com.squareup.okhttp3:mockwebserver3-junit4:5.0.0-alpha.2" testing-mockWebServer = "com.squareup.okhttp3:mockwebserver3-junit4:5.0.0-alpha.2"