From d0bf2a4fb23184316d6cb2063364f65e095fba96 Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Thu, 18 Apr 2024 01:34:28 +0530 Subject: [PATCH] fix: handle user profile navigation correctly --- CHANGELOG.md | 1 + .../android/ui/screens/LobstersPostsScreen.kt | 15 +++++++++- .../claw/android/ui/screens/SearchScreen.kt | 5 ++++ .../claw/common/comments/CommentEntry.kt | 10 +++---- .../claw/common/comments/CommentNode.kt | 23 +++++++++++++-- .../claw/common/comments/Comments.kt | 11 ++++++- .../msfjarvis/claw/common/user/UserProfile.kt | 29 +++++++++++++++++-- 7 files changed, 80 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8dd01ec..9e113fcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fixed a crash when clicking an item on the bottom navigation bar too quickly * Removed buggy deeplinks +* Clicking a username now correctly navigates to the right page in-app ## [1.44.0] - 2024-03-19 diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LobstersPostsScreen.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LobstersPostsScreen.kt index 3caae559..a63271f1 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LobstersPostsScreen.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LobstersPostsScreen.kt @@ -261,6 +261,11 @@ fun LobstersPostsScreen( htmlConverter = htmlConverter, getSeenComments = viewModel::getSeenComments, markSeenComments = viewModel::markSeenComments, + openUserProfile = { + navController.navigate( + Destinations.User.route.replace(Destinations.User.PLACEHOLDER, it) + ) + }, ) } composable( @@ -272,7 +277,15 @@ fun LobstersPostsScreen( "Navigating to ${Destinations.User.route} without necessary 'username' argument" } setWebUri("https://lobste.rs/u/$username") - UserProfile(username = username, getProfile = viewModel::getUserProfile) + UserProfile( + username = username, + getProfile = viewModel::getUserProfile, + openUserProfile = { + navController.navigate( + Destinations.User.route.replace(Destinations.User.PLACEHOLDER, it) + ) + }, + ) } composable(route = Destinations.Settings.route) { SettingsScreen( diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/SearchScreen.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/SearchScreen.kt index 6936186f..a97657f7 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/SearchScreen.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/SearchScreen.kt @@ -67,6 +67,11 @@ fun SearchScreen( htmlConverter = htmlConverter, getSeenComments = viewModel::getSeenComments, markSeenComments = viewModel::markSeenComments, + openUserProfile = { username: String -> + navController.navigate( + Destinations.User.route.replace(Destinations.User.PLACEHOLDER, username) + ) + }, ) } } 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 1562436c..7a9b7b1b 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 @@ -29,7 +29,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString @@ -56,9 +55,9 @@ internal fun CommentsHeader( post: UIPost, postActions: PostActions, htmlConverter: HTMLConverter, + openUserProfile: (String) -> Unit, modifier: Modifier = Modifier, ) { - val uriHandler = LocalUriHandler.current val linkMetadata by produceState(initialValue = LinkMetadata(post.url, null)) { runSuspendCatching { postActions.getLinkMetadata(post.url) } @@ -93,8 +92,7 @@ internal fun CommentsHeader( text = AnnotatedString("Submitted by ${post.submitter}"), avatarUrl = "https://lobste.rs/avatars/${post.submitter}-100.png", contentDescription = "User avatar for ${post.submitter}", - modifier = - Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${post.submitter}") }, + modifier = Modifier.clickable { openUserProfile(post.submitter) }, ) } } @@ -133,9 +131,9 @@ internal fun CommentEntry( commentNode: CommentNode, htmlConverter: HTMLConverter, toggleExpanded: (CommentNode) -> Unit, + openUserProfile: (String) -> Unit, modifier: Modifier = Modifier, ) { - val uriHandler = LocalUriHandler.current val comment = commentNode.comment Box( modifier = @@ -162,7 +160,7 @@ internal fun CommentEntry( ), avatarUrl = "https://lobste.rs/avatars/${comment.user}-100.png", contentDescription = "User avatar for ${comment.user}", - modifier = Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user}") }, + modifier = Modifier.clickable { openUserProfile(comment.user) }, ) if (commentNode.isExpanded) { ThemedRichText( diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt index 4279dc18..2d0daa92 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt @@ -84,9 +84,15 @@ internal fun LazyListScope.nodes( nodes: List, htmlConverter: HTMLConverter, toggleExpanded: (CommentNode) -> Unit, + openUserProfile: (String) -> Unit, ) { nodes.forEach { node -> - node(node = node, htmlConverter = htmlConverter, toggleExpanded = toggleExpanded) + node( + node = node, + htmlConverter = htmlConverter, + toggleExpanded = toggleExpanded, + openUserProfile = openUserProfile, + ) } } @@ -94,16 +100,27 @@ private fun LazyListScope.node( node: CommentNode, htmlConverter: HTMLConverter, toggleExpanded: (CommentNode) -> Unit, + openUserProfile: (String) -> Unit, ) { // Skip the node if neither the node nor its parent is expanded if (!node.isExpanded && node.parent?.isExpanded == false) { return } item { - CommentEntry(commentNode = node, htmlConverter = htmlConverter, toggleExpanded = toggleExpanded) + CommentEntry( + commentNode = node, + htmlConverter = htmlConverter, + toggleExpanded = toggleExpanded, + openUserProfile = openUserProfile, + ) HorizontalDivider() } if (node.children.isNotEmpty()) { - nodes(node.children, htmlConverter = htmlConverter, toggleExpanded = toggleExpanded) + nodes( + node.children, + htmlConverter = htmlConverter, + toggleExpanded = toggleExpanded, + openUserProfile = openUserProfile, + ) } } diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt index b073989c..b449e678 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt @@ -46,6 +46,7 @@ private fun CommentsPageInternal( htmlConverter: HTMLConverter, commentState: PostComments?, markSeenComments: (String, List) -> Unit, + openUserProfile: (String) -> Unit, modifier: Modifier = Modifier, ) { val commentNodes = createListNode(details.comments, commentState).toMutableStateList() @@ -54,7 +55,12 @@ private fun CommentsPageInternal( Surface(color = MaterialTheme.colorScheme.surfaceVariant) { LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) { item { - CommentsHeader(post = details, postActions = postActions, htmlConverter = htmlConverter) + CommentsHeader( + post = details, + postActions = postActions, + htmlConverter = htmlConverter, + openUserProfile = openUserProfile, + ) } if (commentNodes.isNotEmpty()) { @@ -79,6 +85,7 @@ private fun CommentsPageInternal( commentNodes.add(index, parent) } }, + openUserProfile = openUserProfile, ) } else { item { @@ -104,6 +111,7 @@ fun CommentsPage( getSeenComments: suspend (String) -> PostComments?, markSeenComments: (String, List) -> Unit, modifier: Modifier = Modifier, + openUserProfile: (String) -> Unit, ) { val postDetails by produceState(Loading) { @@ -124,6 +132,7 @@ fun CommentsPage( htmlConverter = htmlConverter, commentState = commentState, markSeenComments = markSeenComments, + openUserProfile = openUserProfile, modifier = modifier.fillMaxSize(), ) } diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/user/UserProfile.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/user/UserProfile.kt index e8590838..4639c1fe 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/user/UserProfile.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/user/UserProfile.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.produceState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.LinkAnnotation +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.unit.dp import com.github.michaelbull.result.coroutines.runSuspendCatching import com.github.michaelbull.result.fold @@ -42,6 +44,7 @@ import dev.msfjarvis.claw.model.User fun UserProfile( username: String, getProfile: suspend (username: String) -> User, + openUserProfile: (String) -> Unit, modifier: Modifier = Modifier, ) { val user by @@ -56,7 +59,11 @@ fun UserProfile( } when (user) { is Success<*> -> { - UserProfileInternal(user = (user as Success).data, modifier = modifier) + UserProfileInternal( + user = (user as Success).data, + openUserProfile = openUserProfile, + modifier = modifier, + ) } is Error -> { val error = user as Error @@ -77,7 +84,11 @@ fun UserProfile( } @Composable -private fun UserProfileInternal(user: User, modifier: Modifier = Modifier) { +private fun UserProfileInternal( + user: User, + openUserProfile: (String) -> Unit, + modifier: Modifier = Modifier, +) { Surface(modifier = modifier) { Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -93,7 +104,19 @@ private fun UserProfileInternal(user: User, modifier: Modifier = Modifier) { Text(text = user.username, style = MaterialTheme.typography.displaySmall) ThemedRichText(text = user.about) user.invitedBy?.let { invitedBy -> - ThemedRichText(text = "Invited by [${invitedBy}](https://lobste.rs/u/${user.invitedBy})") + Text( + text = + buildAnnotatedString { + append("Invited by ") + pushLink( + LinkAnnotation.Clickable( + tag = "username", + linkInteractionListener = { openUserProfile(invitedBy) }, + ) + ) + append(invitedBy) + } + ) } } }