Skip to content

Commit aab0970

Browse files
committed
Seek video and scroll back to player when a comment timestamp is tapped
1 parent d0f5e1d commit aab0970

5 files changed

Lines changed: 65 additions & 21 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.material3.Surface
77
import androidx.core.os.bundleOf
88
import androidx.fragment.app.Fragment
99
import androidx.fragment.compose.content
10+
import org.schabi.newpipe.fragments.detail.VideoDetailFragment
1011
import org.schabi.newpipe.ui.components.video.comment.CommentSection
1112
import org.schabi.newpipe.ui.theme.AppTheme
1213
import org.schabi.newpipe.util.KEY_SERVICE_ID
@@ -20,7 +21,11 @@ class CommentsFragment : Fragment() {
2021
) = content {
2122
AppTheme {
2223
Surface {
23-
CommentSection()
24+
CommentSection(
25+
onScrollToPlayer = {
26+
(parentFragment as? VideoDetailFragment)?.scrollToTop()
27+
}
28+
)
2429
}
2530
}
2631
}

app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,15 @@ import org.schabi.newpipe.util.image.ImageStrategy
5555

5656
@OptIn(ExperimentalFoundationApi::class)
5757
@Composable
58-
fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
58+
fun Comment(
59+
comment: CommentsInfoItem,
60+
onCommentAuthorOpened: () -> Unit,
61+
onTimestampClick: ((Int) -> Unit)? = null
62+
) {
5963
val context = LocalContext.current
6064
var isExpanded by rememberSaveable { mutableStateOf(false) }
6165
var showReplies by rememberSaveable { mutableStateOf(false) }
62-
val parsedDescription = rememberParsedDescription(comment.commentText)
66+
val parsedDescription = rememberParsedDescription(comment.commentText, onTimestampClick)
6367

6468
Row(
6569
modifier = Modifier
@@ -187,7 +191,8 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
187191
CommentRepliesDialog(
188192
parentComment = comment,
189193
onDismissRequest = { showReplies = false },
190-
onCommentAuthorOpened = onCommentAuthorOpened
194+
onCommentAuthorOpened = onCommentAuthorOpened,
195+
onTimestampClick = onTimestampClick
191196
)
192197
}
193198
}
@@ -265,7 +270,7 @@ private fun CommentPreview(
265270
) {
266271
AppTheme {
267272
Surface {
268-
Comment(commentsInfoItem) {}
273+
Comment(commentsInfoItem, onCommentAuthorOpened = {})
269274
}
270275
}
271276
}
@@ -277,7 +282,7 @@ private fun CommentListPreview() {
277282
Surface {
278283
Column {
279284
for (comment in CommentPreviewProvider().values) {
280-
Comment(comment) {}
285+
Comment(comment, onCommentAuthorOpened = {})
281286
}
282287
}
283288
}

app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ import org.schabi.newpipe.ui.theme.AppTheme
4545
fun CommentRepliesDialog(
4646
parentComment: CommentsInfoItem,
4747
onDismissRequest: () -> Unit,
48-
onCommentAuthorOpened: () -> Unit
48+
onCommentAuthorOpened: () -> Unit,
49+
onTimestampClick: ((Int) -> Unit)? = null
4950
) {
5051
val coroutineScope = rememberCoroutineScope()
5152
val commentsFlow = remember {
@@ -56,7 +57,7 @@ fun CommentRepliesDialog(
5657
.cachedIn(coroutineScope)
5758
}
5859

59-
CommentRepliesDialog(parentComment, commentsFlow, onDismissRequest, onCommentAuthorOpened)
60+
CommentRepliesDialog(parentComment, commentsFlow, onDismissRequest, onCommentAuthorOpened, onTimestampClick)
6061
}
6162

6263
@OptIn(ExperimentalMaterial3Api::class)
@@ -65,7 +66,8 @@ private fun CommentRepliesDialog(
6566
parentComment: CommentsInfoItem,
6667
commentsFlow: Flow<PagingData<CommentsInfoItem>>,
6768
onDismissRequest: () -> Unit,
68-
onCommentAuthorOpened: () -> Unit
69+
onCommentAuthorOpened: () -> Unit,
70+
onTimestampClick: ((Int) -> Unit)? = null
6971
) {
7072
val comments = commentsFlow.collectAsLazyPagingItems()
7173
val nestedScrollInterop = rememberNestedScrollInteropConnection()
@@ -93,7 +95,8 @@ private fun CommentRepliesDialog(
9395
item {
9496
CommentRepliesHeader(
9597
comment = parentComment,
96-
onCommentAuthorOpened = nestedOnCommentAuthorOpened
98+
onCommentAuthorOpened = nestedOnCommentAuthorOpened,
99+
onTimestampClick = onTimestampClick
97100
)
98101
HorizontalDivider(
99102
thickness = 1.dp,
@@ -145,7 +148,8 @@ private fun CommentRepliesDialog(
145148
items(comments.itemCount) {
146149
Comment(
147150
comment = comments[it]!!,
148-
onCommentAuthorOpened = nestedOnCommentAuthorOpened
151+
onCommentAuthorOpened = nestedOnCommentAuthorOpened,
152+
onTimestampClick = onTimestampClick
149153
)
150154
}
151155
}

app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ import org.schabi.newpipe.util.NavigationHelper
3939
import org.schabi.newpipe.util.image.ImageStrategy
4040

4141
@Composable
42-
fun CommentRepliesHeader(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
42+
fun CommentRepliesHeader(
43+
comment: CommentsInfoItem,
44+
onCommentAuthorOpened: () -> Unit,
45+
onTimestampClick: ((Int) -> Unit)? = null
46+
) {
4347
val context = LocalContext.current
4448

4549
Column(
@@ -126,7 +130,8 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, onCommentAuthorOpened: () ->
126130

127131
DescriptionText(
128132
description = comment.commentText,
129-
style = MaterialTheme.typography.bodyMedium
133+
style = MaterialTheme.typography.bodyMedium,
134+
onTimestampClick = onTimestampClick
130135
)
131136
}
132137
}
@@ -145,7 +150,7 @@ fun CommentRepliesHeaderPreview() {
145150

146151
AppTheme {
147152
Surface {
148-
CommentRepliesHeader(comment) {}
153+
CommentRepliesHeader(comment, onCommentAuthorOpened = {})
149154
}
150155
}
151156
}

app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import androidx.compose.material3.Surface
1313
import androidx.compose.material3.Text
1414
import androidx.compose.runtime.Composable
1515
import androidx.compose.runtime.getValue
16+
import androidx.compose.runtime.remember
17+
import androidx.compose.runtime.rememberUpdatedState
1618
import androidx.compose.ui.Alignment
1719
import androidx.compose.ui.Modifier
1820
import androidx.compose.ui.input.nestedscroll.nestedScroll
21+
import androidx.compose.ui.platform.LocalContext
1922
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
2023
import androidx.compose.ui.res.pluralStringResource
2124
import androidx.compose.ui.tooling.preview.Preview
@@ -30,6 +33,7 @@ import kotlinx.coroutines.flow.flowOf
3033
import org.schabi.newpipe.R
3134
import org.schabi.newpipe.error.ErrorInfo
3235
import org.schabi.newpipe.error.UserAction
36+
import org.schabi.newpipe.extractor.NewPipe
3337
import org.schabi.newpipe.extractor.Page
3438
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
3539
import org.schabi.newpipe.extractor.stream.Description
@@ -39,20 +43,38 @@ import org.schabi.newpipe.ui.components.common.LoadingIndicator
3943
import org.schabi.newpipe.ui.emptystate.EmptyStateComposable
4044
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec
4145
import org.schabi.newpipe.ui.theme.AppTheme
46+
import org.schabi.newpipe.util.text.InternalUrlsHandler
4247
import org.schabi.newpipe.viewmodels.CommentsViewModel
4348
import org.schabi.newpipe.viewmodels.util.Resource
4449

4550
@Composable
46-
fun CommentSection(commentsViewModel: CommentsViewModel = viewModel()) {
51+
fun CommentSection(
52+
commentsViewModel: CommentsViewModel = viewModel(),
53+
onScrollToPlayer: () -> Unit = {}
54+
) {
4755
val state by commentsViewModel.uiState.collectAsStateWithLifecycle()
48-
CommentSection(state, commentsViewModel.comments)
56+
CommentSection(state, commentsViewModel.comments, onScrollToPlayer)
4957
}
5058

5159
@Composable
5260
private fun CommentSection(
5361
uiState: Resource<CommentInfo>,
54-
commentsFlow: Flow<PagingData<CommentsInfoItem>>
62+
commentsFlow: Flow<PagingData<CommentsInfoItem>>,
63+
onScrollToPlayer: () -> Unit = {}
5564
) {
65+
val context = LocalContext.current
66+
val commentInfo = (uiState as? Resource.Success)?.data
67+
val onScrollToPlayerState = rememberUpdatedState(onScrollToPlayer)
68+
val onTimestampClick: ((Int) -> Unit)? = remember(commentInfo?.serviceId, commentInfo?.url) {
69+
if (commentInfo == null) return@remember null
70+
runCatching { NewPipe.getService(commentInfo.serviceId) }.getOrNull()
71+
?.let { service ->
72+
{ seconds: Int ->
73+
InternalUrlsHandler.playOnPopup(context, commentInfo.url, service, seconds)
74+
onScrollToPlayerState.value()
75+
}
76+
}
77+
}
5678
val comments = commentsFlow.collectAsLazyPagingItems()
5779
val nestedScrollInterop = rememberNestedScrollInteropConnection()
5880
val state = rememberLazyListState()
@@ -72,10 +94,9 @@ private fun CommentSection(
7294
}
7395

7496
is Resource.Success -> {
75-
val commentInfo = uiState.data
76-
val count = commentInfo.commentCount
97+
val count = uiState.data.commentCount
7798

78-
if (commentInfo.isCommentsDisabled) {
99+
if (uiState.data.isCommentsDisabled) {
79100
item {
80101
EmptyStateComposable(
81102
spec = EmptyStateSpec.DisabledComments,
@@ -137,7 +158,11 @@ private fun CommentSection(
137158

138159
else -> {
139160
items(comments.itemCount) {
140-
Comment(comment = comments[it]!!) {}
161+
Comment(
162+
comment = comments[it]!!,
163+
onCommentAuthorOpened = {},
164+
onTimestampClick = onTimestampClick
165+
)
141166
}
142167
}
143168
}

0 commit comments

Comments
 (0)