Skip to content

Commit bd222c4

Browse files
Show stream uploader icon on comments they have replied to
1 parent 407d2d7 commit bd222c4

4 files changed

Lines changed: 73 additions & 30 deletions

File tree

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

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ 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+
uploaderAvatarUrl: String? = null,
61+
onCommentAuthorOpened: (() -> Unit)? = null,
62+
) {
5963
val context = LocalContext.current
6064
var isExpanded by rememberSaveable { mutableStateOf(false) }
6165
var showReplies by rememberSaveable { mutableStateOf(false) }
@@ -82,7 +86,7 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
8286
.clip(CircleShape)
8387
.clickable {
8488
NavigationHelper.openCommentAuthorIfPresent(context, comment)
85-
onCommentAuthorOpened()
89+
onCommentAuthorOpened?.invoke()
8690
}
8791
)
8892

@@ -159,17 +163,31 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
159163
}
160164

161165
if (comment.replies != null) {
162-
// reduce LocalMinimumInteractiveComponentSize from 48dp to 44dp to slightly
163-
// reduce the button margin (which is still clickable but not visible)
164-
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 44.dp) {
165-
TextButton(
166-
onClick = { showReplies = true },
167-
modifier = Modifier.padding(end = 2.dp)
168-
) {
169-
val text = pluralStringResource(
170-
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
166+
Row(verticalAlignment = Alignment.CenterVertically) {
167+
if (comment.hasCreatorReply()) {
168+
AsyncImage(
169+
model = uploaderAvatarUrl,
170+
contentDescription = null,
171+
placeholder = painterResource(R.drawable.placeholder_person),
172+
error = painterResource(R.drawable.placeholder_person),
173+
modifier = Modifier
174+
.size(30.dp)
175+
.clip(CircleShape)
171176
)
172-
Text(text = text)
177+
}
178+
179+
// reduce LocalMinimumInteractiveComponentSize from 48dp to 44dp to slightly
180+
// reduce the button margin (which is still clickable but not visible)
181+
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 44.dp) {
182+
TextButton(
183+
onClick = { showReplies = true },
184+
modifier = Modifier.padding(end = 2.dp)
185+
) {
186+
val text = pluralStringResource(
187+
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
188+
)
189+
Text(text = text)
190+
}
173191
}
174192
}
175193
}
@@ -198,6 +216,7 @@ fun CommentsInfoItem(
198216
isPinned: Boolean = false,
199217
replies: Page? = null,
200218
replyCount: Int = 0,
219+
hasCreatorReply: Boolean = false,
201220
) = CommentsInfoItem(serviceId, url, name).apply {
202221
this.commentText = commentText
203222
this.uploaderName = uploaderName
@@ -207,6 +226,7 @@ fun CommentsInfoItem(
207226
this.isPinned = isPinned
208227
this.replies = replies
209228
this.replyCount = replyCount
229+
setCreatorReply(hasCreatorReply)
210230
}
211231

212232
private class CommentPreviewProvider : CollectionPreviewParameterProvider<CommentsInfoItem>(
@@ -245,7 +265,8 @@ private class CommentPreviewProvider : CollectionPreviewParameterProvider<Commen
245265
isPinned = false,
246266
isHeartedByUploader = false,
247267
replies = Page(""),
248-
replyCount = 4283
268+
replyCount = 4283,
269+
hasCreatorReply = true,
249270
),
250271
)
251272
)
@@ -258,7 +279,7 @@ private fun CommentPreview(
258279
) {
259280
AppTheme {
260281
Surface {
261-
Comment(commentsInfoItem) {}
282+
Comment(commentsInfoItem)
262283
}
263284
}
264285
}
@@ -270,7 +291,7 @@ private fun CommentListPreview() {
270291
Surface {
271292
Column {
272293
for (comment in CommentPreviewProvider().values) {
273-
Comment(comment) {}
294+
Comment(comment)
274295
}
275296
}
276297
}

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import org.schabi.newpipe.ui.theme.AppTheme
4444
fun CommentRepliesDialog(
4545
parentComment: CommentsInfoItem,
4646
onDismissRequest: () -> Unit,
47-
onCommentAuthorOpened: () -> Unit,
47+
onCommentAuthorOpened: (() -> Unit)?,
4848
) {
4949
val coroutineScope = rememberCoroutineScope()
5050
val commentsFlow = remember {
@@ -64,7 +64,7 @@ private fun CommentRepliesDialog(
6464
parentComment: CommentsInfoItem,
6565
commentsFlow: Flow<PagingData<CommentsInfoItem>>,
6666
onDismissRequest: () -> Unit,
67-
onCommentAuthorOpened: () -> Unit,
67+
onCommentAuthorOpened: (() -> Unit)? = null,
6868
) {
6969
val comments = commentsFlow.collectAsLazyPagingItems()
7070
val nestedScrollInterop = rememberNestedScrollInteropConnection()
@@ -74,7 +74,7 @@ private fun CommentRepliesDialog(
7474
val sheetState = rememberModalBottomSheetState()
7575
val nestedOnCommentAuthorOpened: () -> Unit = {
7676
// also partialExpand any parent dialog
77-
onCommentAuthorOpened()
77+
onCommentAuthorOpened?.invoke()
7878
coroutineScope.launch {
7979
sheetState.partialExpand()
8080
}
@@ -138,10 +138,7 @@ private fun CommentRepliesDialog(
138138
}
139139
} else {
140140
items(comments.itemCount) {
141-
Comment(
142-
comment = comments[it]!!,
143-
onCommentAuthorOpened = nestedOnCommentAuthorOpened,
144-
)
141+
Comment(comments[it]!!, onCommentAuthorOpened = nestedOnCommentAuthorOpened)
145142
}
146143
}
147144
}
@@ -172,6 +169,6 @@ private fun CommentRepliesDialogPreview() {
172169
val flow = flowOf(PagingData.from(replies))
173170

174171
AppTheme {
175-
CommentRepliesDialog(comment, flow, onDismissRequest = {}, onCommentAuthorOpened = {})
172+
CommentRepliesDialog(comment, flow, onDismissRequest = {})
176173
}
177174
}

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,25 @@ import org.schabi.newpipe.ui.components.common.LoadingIndicator
3232
import org.schabi.newpipe.ui.emptystate.EmptyStateComposable
3333
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec
3434
import org.schabi.newpipe.ui.theme.AppTheme
35+
import org.schabi.newpipe.util.image.ImageStrategy
3536
import org.schabi.newpipe.viewmodels.CommentsViewModel
3637
import org.schabi.newpipe.viewmodels.util.Resource
3738

3839
@Composable
3940
fun CommentSection(commentsViewModel: CommentsViewModel = viewModel()) {
40-
val state by commentsViewModel.uiState.collectAsStateWithLifecycle()
41-
CommentSection(state, commentsViewModel.comments)
41+
val streamState by commentsViewModel.streamState.collectAsStateWithLifecycle()
42+
val commentState by commentsViewModel.commentState.collectAsStateWithLifecycle()
43+
44+
val avatars = (streamState as? Resource.Success)?.data?.uploaderAvatars.orEmpty()
45+
val uploaderAvatarUrl = ImageStrategy.choosePreferredImage(avatars)
46+
47+
CommentSection(commentState, uploaderAvatarUrl, commentsViewModel.comments)
4248
}
4349

4450
@Composable
4551
private fun CommentSection(
4652
uiState: Resource<CommentInfo>,
53+
uploaderAvatarUrl: String? = null,
4754
commentsFlow: Flow<PagingData<CommentsInfoItem>>
4855
) {
4956
val comments = commentsFlow.collectAsLazyPagingItems()
@@ -110,7 +117,7 @@ private fun CommentSection(
110117

111118
else -> {
112119
items(comments.itemCount) {
113-
Comment(comment = comments[it]!!) {}
120+
Comment(comments[it]!!, uploaderAvatarUrl)
114121
}
115122
}
116123
}
@@ -140,7 +147,7 @@ private fun CommentSection(
140147
private fun CommentSectionLoadingPreview() {
141148
AppTheme {
142149
Surface {
143-
CommentSection(uiState = Resource.Loading, commentsFlow = flowOf())
150+
CommentSection(Resource.Loading, commentsFlow = flowOf())
144151
}
145152
}
146153
}
@@ -187,7 +194,7 @@ private fun CommentSectionSuccessPreview() {
187194
private fun CommentSectionErrorPreview() {
188195
AppTheme {
189196
Surface {
190-
CommentSection(uiState = Resource.Error(RuntimeException()), commentsFlow = flowOf())
197+
CommentSection(Resource.Error(RuntimeException()), commentsFlow = flowOf())
191198
}
192199
}
193200
}

app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,32 @@ import kotlinx.coroutines.flow.flatMapLatest
1414
import kotlinx.coroutines.flow.flowOn
1515
import kotlinx.coroutines.flow.map
1616
import kotlinx.coroutines.flow.stateIn
17+
import kotlinx.coroutines.rx3.await
1718
import org.schabi.newpipe.extractor.comments.CommentsInfo
1819
import org.schabi.newpipe.paging.CommentsSource
1920
import org.schabi.newpipe.ui.components.video.comment.CommentInfo
21+
import org.schabi.newpipe.util.ExtractorHelper
22+
import org.schabi.newpipe.util.KEY_SERVICE_ID
2023
import org.schabi.newpipe.util.KEY_URL
24+
import org.schabi.newpipe.util.NO_SERVICE_ID
2125
import org.schabi.newpipe.viewmodels.util.Resource
2226

2327
class CommentsViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
24-
val uiState = savedStateHandle.getStateFlow(KEY_URL, "")
28+
private val url = savedStateHandle.getStateFlow(KEY_URL, "")
29+
private val serviceId = savedStateHandle[KEY_SERVICE_ID] ?: NO_SERVICE_ID
30+
31+
val streamState = url
32+
.map {
33+
try {
34+
Resource.Success(ExtractorHelper.getStreamInfo(serviceId, it, false).await())
35+
} catch (e: Exception) {
36+
Resource.Error(e)
37+
}
38+
}
39+
.flowOn(Dispatchers.IO)
40+
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Resource.Loading)
41+
42+
val commentState = url
2543
.map {
2644
try {
2745
Resource.Success(CommentInfo(CommentsInfo.getInfo(it)))
@@ -33,7 +51,7 @@ class CommentsViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
3351
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Resource.Loading)
3452

3553
@OptIn(ExperimentalCoroutinesApi::class)
36-
val comments = uiState
54+
val comments = commentState
3755
.filterIsInstance<Resource.Success<CommentInfo>>()
3856
.flatMapLatest {
3957
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {

0 commit comments

Comments
 (0)