diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt index ec5fc2c5..b2a55b94 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt @@ -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, ) } diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt index de83215a..b0f32422 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt @@ -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) diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt index 23c15893..1d942f31 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt @@ -62,6 +62,7 @@ fun DatabasePosts( ListItem( item = item, isSaved = { true }, + isRead = { false }, postActions = postActions, ) HorizontalDivider() diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/ListItem.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/ListItem.kt index d6e7e465..430d13d5 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/ListItem.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/ListItem.kt @@ -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, ) diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/NetworkPosts.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/NetworkPosts.kt index 176f3940..79909258 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/NetworkPosts.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/NetworkPosts.kt @@ -42,6 +42,7 @@ fun NetworkPosts( lazyPagingItems: LazyPagingItems, 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, ) diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt index 573499b1..41aed9b8 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt @@ -75,6 +75,7 @@ fun SearchList( lazyPagingItems = lazyPagingItems, listState = listState, isPostSaved = isPostSaved, + isPostRead = { false }, postActions = postActions, modifier = modifier, ) diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt index 8cc8904d..90d121a9 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt @@ -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 diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ReadPostsRepository.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ReadPostsRepository.kt new file mode 100644 index 00000000..6735dc0c --- /dev/null +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ReadPostsRepository.kt @@ -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 } +} diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt index 2350564a..75b5bc6b 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt @@ -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)) } diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/LobstersCard.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/LobstersCard.kt index 7066d65d..db04e68d 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/LobstersCard.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/LobstersCard.kt @@ -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) {} diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/PostActions.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/PostActions.kt index bec39e0b..25c2a776 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/PostActions.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/posts/PostActions.kt @@ -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)