app: switch to new models

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2021-02-28 19:08:12 +05:30
parent 9d684536a1
commit 7b87792d8a
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
9 changed files with 74 additions and 86 deletions

View file

@ -2,8 +2,8 @@ package dev.msfjarvis.lobsters.data.remote
import androidx.paging.PagingSource
import androidx.paging.PagingState
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
import dev.msfjarvis.lobsters.model.LobstersPost
import javax.inject.Inject
class LobstersPagingSource @Inject constructor(

View file

@ -1,7 +1,8 @@
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.SavedPost
import dev.msfjarvis.lobsters.model.LobstersPost
import dev.msfjarvis.lobsters.database.LobstersDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@ -13,7 +14,7 @@ class LobstersRepository constructor(
private val lobstersDatabase: LobstersDatabase,
) {
private val savedPostsCache: MutableMap<String, LobstersPost> = mutableMapOf()
private val savedPostsCache: MutableMap<String, SavedPost> = mutableMapOf()
private val _isCacheReady = MutableStateFlow(false)
val isCacheReady = _isCacheReady.asStateFlow()
@ -21,7 +22,7 @@ class LobstersRepository constructor(
return savedPostsCache.containsKey(postId)
}
fun getAllPostsFromCache(): List<LobstersPost> {
fun getAllPostsFromCache(): List<SavedPost> {
return savedPostsCache.values.toList()
}
@ -31,29 +32,29 @@ class LobstersRepository constructor(
suspend fun updateCache() {
if (_isCacheReady.value) return
val posts = getAllPosts()
val posts = getSavedPosts()
posts.forEach {
savedPostsCache[it.short_id] = it
savedPostsCache[it.shortId] = it
}
_isCacheReady.value = true
}
private suspend fun getAllPosts(): List<LobstersPost> = withContext(Dispatchers.IO) {
return@withContext lobstersDatabase.postQueries.selectAllPosts().executeAsList()
private suspend fun getSavedPosts(): List<SavedPost> = withContext(Dispatchers.IO) {
return@withContext lobstersDatabase.savedPostQueries.selectAllPosts().executeAsList()
}
suspend fun addPost(post: LobstersPost) = withContext(Dispatchers.IO) {
if (!savedPostsCache.containsKey(post.short_id)) {
savedPostsCache.putIfAbsent(post.short_id, post)
lobstersDatabase.postQueries.insertOrReplacePost(post)
suspend fun addPost(post: SavedPost) = withContext(Dispatchers.IO) {
if (!savedPostsCache.containsKey(post.shortId)) {
savedPostsCache.putIfAbsent(post.shortId, post)
lobstersDatabase.savedPostQueries.insertOrReplacePost(post)
}
}
suspend fun removePost(post: LobstersPost) = withContext(Dispatchers.IO) {
if (savedPostsCache.containsKey(post.short_id)) {
savedPostsCache.remove(post.short_id)
lobstersDatabase.postQueries.deletePost(post.short_id)
suspend fun removePost(post: SavedPost) = withContext(Dispatchers.IO) {
if (savedPostsCache.containsKey(post.shortId)) {
savedPostsCache.remove(post.shortId)
lobstersDatabase.savedPostQueries.deletePost(post.shortId)
}
}
}

View file

@ -1,7 +1,6 @@
package dev.msfjarvis.lobsters.injection
import android.content.Context
import com.squareup.moshi.JsonAdapter
import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.squareup.sqldelight.db.SqlDriver
import dagger.Module
@ -10,10 +9,8 @@ import dagger.Reusable
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.data.local.SavedPost
import dev.msfjarvis.lobsters.database.LobstersDatabase
import dev.msfjarvis.lobsters.model.Submitter
import dev.msfjarvis.lobsters.model.SubmitterAdapter
import dev.msfjarvis.lobsters.model.TagsAdapter
import javax.inject.Singleton
@ -21,12 +18,6 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Reusable
fun providesSubmitterAdapter(jsonAdapter: JsonAdapter<Submitter>): SubmitterAdapter {
return SubmitterAdapter(jsonAdapter)
}
@Provides
@Reusable
fun providesTagsAdapter(): TagsAdapter {
@ -43,12 +34,11 @@ object DatabaseModule {
@Singleton
fun providesLobstersDatabase(
sqlDriver: SqlDriver,
submitterAdapter: SubmitterAdapter,
tagsAdapter: TagsAdapter
): LobstersDatabase {
return LobstersDatabase(
sqlDriver,
LobstersPost.Adapter(submitterAdapter, tagsAdapter)
SavedPost.Adapter(tagsAdapter),
)
}
}

View file

@ -1,14 +1,11 @@
package dev.msfjarvis.lobsters.injection
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import dagger.Module
import dagger.Provides
import dagger.Reusable
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dev.msfjarvis.lobsters.model.Submitter
import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory
@Module
@ -21,11 +18,4 @@ object MoshiModule {
.add(MetadataKotlinJsonAdapterFactory())
.build()
}
@OptIn(ExperimentalStdlibApi::class)
@Provides
@Reusable
fun provideSubmitterJsonAdapter(moshi: Moshi): JsonAdapter<Submitter> {
return moshi.adapter()
}
}

View file

@ -11,8 +11,10 @@ import androidx.compose.ui.Modifier
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.items
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.model.LobstersPost
import dev.msfjarvis.lobsters.ui.urllauncher.LocalUrlLauncher
import dev.msfjarvis.lobsters.util.toDbModel
@Composable
fun HottestPosts(
@ -20,7 +22,7 @@ fun HottestPosts(
listState: LazyListState,
isPostSaved: (String) -> Boolean,
modifier: Modifier = Modifier,
saveAction: (LobstersPost) -> Unit,
saveAction: (SavedPost) -> Unit,
) {
val urlLauncher = LocalUrlLauncher.current
@ -33,13 +35,15 @@ fun HottestPosts(
) {
items(posts) { item ->
if (item != null) {
var isSaved by remember(item.short_id) { mutableStateOf(isPostSaved(item.short_id)) }
@Suppress("NAME_SHADOWING")
val item = item.toDbModel()
var isSaved by remember(item.shortId) { mutableStateOf(isPostSaved(item.shortId)) }
LobstersItem(
post = item,
isSaved = isSaved,
onClick = { urlLauncher.launch(item.url.ifEmpty { item.comments_url }) },
onLongClick = { urlLauncher.launch(item.comments_url) },
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
onLongClick = { urlLauncher.launch(item.commentsUrl) },
onSaveButtonClick = {
isSaved = isSaved.not()
saveAction.invoke(item)

View file

@ -29,43 +29,26 @@ import coil.transform.CircleCropTransformation
import dev.chrisbanes.accompanist.coil.CoilImage
import dev.msfjarvis.lobsters.R
import dev.msfjarvis.lobsters.data.api.LobstersApi
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.model.Submitter
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.ui.theme.LobstersTheme
import dev.msfjarvis.lobsters.ui.theme.titleColor
import dev.msfjarvis.lobsters.util.IconResource
val TEST_POST = LobstersPost(
"zqyydb",
"https://lobste.rs/s/zqyydb",
"2020-09-21T07:11:14.000-05:00",
"k2k20 hackathon report: Bob Beck on LibreSSL progress",
"https://undeadly.org/cgi?action=article;sid=20200921105847",
4,
0,
0,
"",
"https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
Submitter(
"Vigdis",
"2017-02-27T21:08:14.000-06:00",
false,
"Alleycat for the fun, sys/net admin for a living and OpenBSD contributions for the pleasure. (Not so) French dude in Montreal\r\n\r\nhttps://chown.me",
false,
76,
"/avatars/Vigdis-100.png",
"sevan",
null,
null,
emptyList(),
),
listOf("openbsd", "linux", "containers", "hack the planet", "no thanks"),
val TEST_POST = SavedPost(
shortId = "zqyydb",
title = "k2k20 hackathon report: Bob Beck on LibreSSL progress",
url = "https://undeadly.org/cgi?action=article;sid=20200921105847",
createdAt = "2020-09-21T07:11:14.000-05:00",
commentsUrl = "https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
submitterName = "Vigdis",
submitterAvatarUrl = "/avatars/Vigdis-100.png",
tags = listOf("openbsd", "linux", "containers", "hack the planet", "no thanks"),
)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LobstersItem(
post: LobstersPost,
post: SavedPost,
isSaved: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
@ -99,10 +82,10 @@ fun LobstersItem(
)
Row {
CoilImage(
data = "${LobstersApi.BASE_URL}/${post.submitter_user.avatarUrl}",
data = "${LobstersApi.BASE_URL}/${post.submitterAvatarUrl}",
contentDescription = stringResource(
R.string.avatar_content_description,
post.submitter_user.username
post.submitterName
),
fadeIn = true,
requestBuilder = {
@ -113,7 +96,7 @@ fun LobstersItem(
.padding(4.dp),
)
Text(
text = stringResource(id = R.string.submitted_by, post.submitter_user.username),
text = stringResource(id = R.string.submitted_by, post.submitterName),
modifier = Modifier
.padding(4.dp),
)

View file

@ -6,16 +6,16 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.ui.urllauncher.LocalUrlLauncher
import dev.msfjarvis.lobsters.util.asZonedDateTime
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SavedPosts(
posts: List<LobstersPost>,
posts: List<SavedPost>,
modifier: Modifier = Modifier,
saveAction: (LobstersPost) -> Unit,
saveAction: (SavedPost) -> Unit,
) {
val listState = rememberLazyListState()
val urlLauncher = LocalUrlLauncher.current
@ -27,7 +27,7 @@ fun SavedPosts(
state = listState,
modifier = Modifier.then(modifier),
) {
val grouped = posts.groupBy { it.created_at.asZonedDateTime().month }
val grouped = posts.groupBy { it.createdAt.asZonedDateTime().month }
grouped.forEach { (month, posts) ->
stickyHeader {
MonthHeader(month = month)
@ -36,8 +36,8 @@ fun SavedPosts(
LobstersItem(
post = item,
isSaved = true,
onClick = { urlLauncher.launch(item.url.ifEmpty { item.comments_url }) },
onLongClick = { urlLauncher.launch(item.comments_url) },
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
onLongClick = { urlLauncher.launch(item.commentsUrl) },
onSaveButtonClick = { saveAction.invoke(item) },
)
}

View file

@ -6,7 +6,7 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.data.remote.LobstersPagingSource
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
import javax.inject.Inject
@ -21,7 +21,7 @@ class LobstersViewModel @Inject constructor(
private val lobstersRepository: LobstersRepository,
private val pagingSource: LobstersPagingSource,
) : ViewModel() {
private val _savedPosts = MutableStateFlow<List<LobstersPost>>(emptyList())
private val _savedPosts = MutableStateFlow<List<SavedPost>>(emptyList())
val savedPosts = _savedPosts.asStateFlow()
val posts = Pager(PagingConfig(25)) {
pagingSource
@ -35,9 +35,9 @@ class LobstersViewModel @Inject constructor(
}.launchIn(viewModelScope)
}
fun toggleSave(post: LobstersPost) {
fun toggleSave(post: SavedPost) {
viewModelScope.launch {
val isSaved = lobstersRepository.isPostSaved(post.short_id)
val isSaved = lobstersRepository.isPostSaved(post.shortId)
if (isSaved) removeSavedPost(post) else savePost(post)
}
}
@ -46,14 +46,14 @@ class LobstersViewModel @Inject constructor(
return lobstersRepository.isPostSaved(postId)
}
private fun savePost(post: LobstersPost) {
private fun savePost(post: SavedPost) {
viewModelScope.launch {
lobstersRepository.addPost(post)
_savedPosts.value = lobstersRepository.getAllPostsFromCache()
}
}
private fun removeSavedPost(post: LobstersPost) {
private fun removeSavedPost(post: SavedPost) {
viewModelScope.launch {
lobstersRepository.removePost(post)
_savedPosts.value = lobstersRepository.getAllPostsFromCache()

View file

@ -0,0 +1,20 @@
package dev.msfjarvis.lobsters.util
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.model.LobstersPost
/**
* Convert a [LobstersPost] object returned by the API into a [SavedPost] for persistence.
*/
fun LobstersPost.toDbModel(): SavedPost {
return SavedPost(
shortId = shortId,
title = title,
url = url,
createdAt = createdAt,
commentsUrl = commentsUrl,
submitterName = submitterUser.username,
submitterAvatarUrl = submitterUser.avatarUrl,
tags = tags,
)
}