feat(android): add support for identifying read posts

This commit is contained in:
Harsh Shandilya 2023-08-16 00:02:24 +05:30
parent 9c32d6721b
commit bc2365b08b
No known key found for this signature in database
11 changed files with 62 additions and 10 deletions

View file

@ -218,6 +218,7 @@ fun LobstersApp(
lazyPagingItems = hottestPosts,
listState = hottestListState,
isPostSaved = viewModel::isPostSaved,
isPostRead = viewModel::isPostRead,
postActions = postActions,
)
}
@ -227,6 +228,7 @@ fun LobstersApp(
lazyPagingItems = newestPosts,
listState = newestListState,
isPostSaved = viewModel::isPostSaved,
isPostRead = viewModel::isPostRead,
postActions = postActions,
)
}

View file

@ -36,11 +36,13 @@ fun rememberPostActions(
): PostActions {
return remember {
object : PostActions {
override fun viewPost(postUrl: String, commentsUrl: String) {
override fun viewPost(postId: String, postUrl: String, commentsUrl: String) {
viewModel.markPostAsRead(postId)
urlLauncher.openUri(postUrl.ifEmpty { commentsUrl })
}
override fun viewComments(postId: String) {
viewModel.markPostAsRead(postId)
val currentRoute = navController.currentDestination?.route
val newRoute =
Destinations.Comments.route.replace(Destinations.Comments.placeholder, postId)

View file

@ -62,6 +62,7 @@ fun DatabasePosts(
ListItem(
item = item,
isSaved = { true },
isRead = { false },
postActions = postActions,
)
HorizontalDivider()

View file

@ -24,10 +24,12 @@ import me.saket.swipe.SwipeableActionsBox
fun ListItem(
item: SavedPost,
isSaved: suspend (SavedPost) -> Boolean,
isRead: suspend (String) -> Boolean,
postActions: PostActions,
modifier: Modifier = Modifier,
) {
val saved by produceState(false, item) { value = isSaved(item) }
val read by produceState(false, item.shortId) { value = isRead(item.shortId) }
val commentsAction =
SwipeAction(
icon = rememberVectorPainter(Icons.Filled.Reply),
@ -40,6 +42,7 @@ fun ListItem(
LobstersCard(
post = item,
isSaved = saved,
isRead = read,
postActions = postActions,
modifier = modifier,
)

View file

@ -42,6 +42,7 @@ fun NetworkPosts(
lazyPagingItems: LazyPagingItems<LobstersPost>,
listState: LazyListState,
isPostSaved: suspend (SavedPost) -> Boolean,
isPostRead: suspend (String) -> Boolean,
postActions: PostActions,
modifier: Modifier = Modifier,
) {
@ -71,6 +72,7 @@ fun NetworkPosts(
ListItem(
item = dbModel,
isSaved = isPostSaved,
isRead = isPostRead,
postActions = postActions,
)

View file

@ -75,6 +75,7 @@ fun SearchList(
lazyPagingItems = lazyPagingItems,
listState = listState,
isPostSaved = isPostSaved,
isPostRead = { false },
postActions = postActions,
modifier = modifier,
)

View file

@ -48,8 +48,9 @@ class ClawViewModel
constructor(
private val api: LobstersApi,
private val searchApi: LobstersSearchApi,
private val savedPostsRepository: SavedPostsRepository,
private val commentsRepository: CommentsRepository,
private val readPostsRepository: ReadPostsRepository,
private val savedPostsRepository: SavedPostsRepository,
private val linkMetadataRepository: LinkMetadataRepository,
private val dataTransferRepository: DataTransferRepository,
private val pagingSourceFactory: LobstersPagingSource.Factory,
@ -147,6 +148,12 @@ constructor(
suspend fun exportPosts(output: OutputStream) = dataTransferRepository.exportPosts(output)
fun markPostAsRead(postId: String) {
viewModelScope.launch { readPostsRepository.markRead(postId) }
}
suspend fun isPostRead(postId: String) = readPostsRepository.isRead(postId)
/**
* Parses a given [String] into a [LocalDateTime]. This method is only intended to be used for
* dates in the format returned by the Lobsters API, and is not a general purpose parsing

View file

@ -0,0 +1,28 @@
/*
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.viewmodel
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher
import dev.msfjarvis.claw.database.local.ReadPostsQueries
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
class ReadPostsRepository
@Inject
constructor(
private val readPostsQueries: ReadPostsQueries,
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
) {
suspend fun markRead(postId: String) {
withContext(dbDispatcher) { readPostsQueries.markRead(postId) }
}
suspend fun isRead(postId: String): Boolean =
withContext(dbDispatcher) { readPostsQueries.isRead(postId).executeAsOneOrNull() != null }
}

View file

@ -70,7 +70,7 @@ internal fun CommentsHeader(
modifier = Modifier.padding(16.dp).fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
PostTitle(title = postDetails.title)
PostTitle(title = postDetails.title, isRead = false)
TagRow(tags = postDetails.tags.toImmutableList())
Spacer(Modifier.height(4.dp))
@ -78,7 +78,9 @@ internal fun CommentsHeader(
PostLink(
linkMetadata = linkMetadata,
modifier =
Modifier.clickable { postActions.viewPost(linkMetadata.url, postDetails.commentsUrl) },
Modifier.clickable {
postActions.viewPost(postDetails.shortId, linkMetadata.url, postDetails.commentsUrl)
},
)
Spacer(Modifier.height(4.dp))
}

View file

@ -62,6 +62,7 @@ import kotlinx.collections.immutable.toImmutableList
fun LobstersCard(
post: SavedPost,
isSaved: Boolean,
isRead: Boolean,
postActions: PostActions,
modifier: Modifier = Modifier,
) {
@ -70,7 +71,7 @@ fun LobstersCard(
modifier =
modifier
.fillMaxWidth()
.clickable { postActions.viewPost(post.url, post.commentsUrl) }
.clickable { postActions.viewPost(post.shortId, post.url, post.commentsUrl) }
.background(MaterialTheme.colorScheme.background)
.padding(start = 16.dp, top = 16.dp, end = 4.dp, bottom = 16.dp),
) {
@ -81,6 +82,7 @@ fun LobstersCard(
PostDetails(
modifier = Modifier.weight(1f),
post = post,
isRead = isRead,
)
Column(
modifier = Modifier.wrapContentHeight(),
@ -110,9 +112,9 @@ fun LobstersCard(
}
@Composable
fun PostDetails(post: SavedPost, modifier: Modifier = Modifier) {
fun PostDetails(post: SavedPost, isRead: Boolean, modifier: Modifier = Modifier) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
PostTitle(title = post.title)
PostTitle(title = post.title, isRead = isRead)
TagRow(tags = post.tags.toImmutableList())
Spacer(Modifier.height(4.dp))
Submitter(
@ -126,13 +128,14 @@ fun PostDetails(post: SavedPost, modifier: Modifier = Modifier) {
@Composable
internal fun PostTitle(
title: String,
isRead: Boolean,
modifier: Modifier = Modifier,
) {
Text(
text = title,
modifier = modifier,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
fontWeight = if (isRead) FontWeight.Normal else FontWeight.Bold,
color = MaterialTheme.colorScheme.onBackground,
)
}
@ -261,10 +264,11 @@ fun LobstersCardPreview() {
tags = listOf("databases", "apis"),
description = "",
),
isRead = true,
isSaved = true,
postActions =
object : PostActions {
override fun viewPost(postUrl: String, commentsUrl: String) {}
override fun viewPost(postId: String, postUrl: String, commentsUrl: String) {}
override fun viewComments(postId: String) {}

View file

@ -13,7 +13,7 @@ import dev.msfjarvis.claw.model.LobstersPostDetails
@Stable
interface PostActions {
fun viewPost(postUrl: String, commentsUrl: String)
fun viewPost(postId: String, postUrl: String, commentsUrl: String)
fun viewComments(postId: String)