11package project.pipepipe.app.ui.screens.videodetail
22
33import androidx.activity.compose.BackHandler
4- import androidx.compose.foundation.layout.Box
4+ import androidx.compose.foundation.ExperimentalFoundationApi
55import androidx.compose.foundation.layout.fillMaxSize
6+ import androidx.compose.foundation.lazy.LazyColumn
67import androidx.compose.foundation.lazy.LazyListState
7- import androidx.compose.runtime.Composable
8- import androidx.compose.runtime.DisposableEffect
9- import androidx.compose.runtime.collectAsState
10- import androidx.compose.runtime.getValue
11- import androidx.compose.runtime.remember
12- import androidx.compose.runtime.rememberCoroutineScope
8+ import androidx.compose.runtime.*
139import androidx.compose.ui.Modifier
1410import androidx.navigation.NavHostController
11+ import kotlinx.coroutines.flow.distinctUntilChanged
1512import kotlinx.coroutines.launch
1613import project.pipepipe.app.SharedContext
1714import project.pipepipe.app.ui.component.ErrorComponent
18- import project.pipepipe.app.ui.list.CommentList
15+ import project.pipepipe.app.ui.list.commentListContent
16+ import project.pipepipe.shared.infoitem.StreamInfo
1917
18+ @OptIn(ExperimentalFoundationApi ::class )
2019@Composable
2120fun CommentSection (
22- modifier : Modifier = Modifier ,
23- onTimestampClick : (Long ) -> Unit ,
21+ streamInfo : StreamInfo ,
2422 navController : NavHostController ,
23+ onPlayAudioClick : () -> Unit ,
24+ onAddToPlaylistClick : () -> Unit ,
25+ onTimestampClick : (Long ) -> Unit ,
26+ modifier : Modifier = Modifier
2527) {
2628 val viewModel = SharedContext .sharedVideoDetailViewModel
2729 val uiState by viewModel.uiState.collectAsState()
@@ -58,24 +60,56 @@ fun CommentSection(
5860
5961 val serviceId = uiState.currentStreamInfo?.serviceId
6062
61- Box (modifier = modifier.fillMaxSize()) {
62- if (commentsState.common.error != null ) {
63- ErrorComponent (
64- error = commentsState.common.error!! ,
65- onRetry = {
66- uiState.currentStreamInfo?.let { streamInfo ->
67- coroutineScope.launch {
68- viewModel.loadComments(streamInfo)
69- }
63+ if (commentsState.common.error != null ) {
64+ ErrorComponent (
65+ error = commentsState.common.error!! ,
66+ onRetry = {
67+ uiState.currentStreamInfo?.let { streamInfo ->
68+ coroutineScope.launch {
69+ viewModel.loadComments(streamInfo)
7070 }
71- },
72- modifier = Modifier .fillMaxSize(),
73- shouldStartFromTop = true
74- )
75- } else {
76- when {
77- commentsState.parentComment != null -> {
78- CommentList (
71+ }
72+ },
73+ modifier = modifier.fillMaxSize(),
74+ shouldStartFromTop = true
75+ )
76+ } else {
77+ when {
78+ commentsState.parentComment != null -> {
79+ // Replies view with common header
80+ LaunchedEffect (repliesListState, commentsState.replies.nextPageUrl, commentsState.common.isLoading) {
81+ snapshotFlow {
82+ val layoutInfo = repliesListState.layoutInfo
83+ val lastVisibleIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ? : - 1
84+ val totalItemsCount = layoutInfo.totalItemsCount
85+ lastVisibleIndex to totalItemsCount
86+ }
87+ .distinctUntilChanged()
88+ .collect { (lastVisibleIndex, totalItemsCount) ->
89+ val isAtBottom = totalItemsCount > 0 && lastVisibleIndex == totalItemsCount - 1
90+ val hasMoreReplies = commentsState.replies.nextPageUrl != null
91+ if (isAtBottom && hasMoreReplies && ! commentsState.common.isLoading) {
92+ serviceId?.let {
93+ viewModel.loadMoreReplies(it)
94+ }
95+ }
96+ }
97+ }
98+
99+ LazyColumn (
100+ modifier = modifier.fillMaxSize(),
101+ state = repliesListState
102+ ) {
103+ // Common header
104+ videoDetailCommonHeader(
105+ streamInfo = streamInfo,
106+ navController = navController,
107+ onPlayAudioClick = onPlayAudioClick,
108+ onAddToPlaylistClick = onAddToPlaylistClick
109+ )
110+
111+ // Replies content (inline from CommentList)
112+ commentListContent(
79113 comments = commentsState.replies.itemList,
80114 isLoading = commentsState.common.isLoading,
81115 hasMoreComments = commentsState.replies.nextPageUrl != null ,
@@ -89,16 +123,49 @@ fun CommentSection(
89123 },
90124 showStickyHeader = true ,
91125 onBackClick = { viewModel.backToCommentList() },
92- listState = repliesListState,
93126 navController = navController,
94127 onTimestampClick = onTimestampClick
95128 )
96- BackHandler () {
97- viewModel.backToCommentList()
129+ }
130+ BackHandler {
131+ viewModel.backToCommentList()
132+ }
133+ }
134+ else -> {
135+ // Comments list with common header
136+ LaunchedEffect (commentsListState, commentsState.comments.nextPageUrl, commentsState.common.isLoading) {
137+ snapshotFlow {
138+ val layoutInfo = commentsListState.layoutInfo
139+ val lastVisibleIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ? : - 1
140+ val totalItemsCount = layoutInfo.totalItemsCount
141+ lastVisibleIndex to totalItemsCount
98142 }
143+ .distinctUntilChanged()
144+ .collect { (lastVisibleIndex, totalItemsCount) ->
145+ val isAtBottom = totalItemsCount > 0 && lastVisibleIndex == totalItemsCount - 1
146+ val hasMoreComments = commentsState.comments.nextPageUrl != null
147+ if (isAtBottom && hasMoreComments && ! commentsState.common.isLoading) {
148+ serviceId?.let {
149+ viewModel.loadMoreComments(it)
150+ }
151+ }
152+ }
99153 }
100- else -> {
101- CommentList (
154+
155+ LazyColumn (
156+ modifier = modifier.fillMaxSize(),
157+ state = commentsListState
158+ ) {
159+ // Common header
160+ videoDetailCommonHeader(
161+ streamInfo = streamInfo,
162+ navController = navController,
163+ onPlayAudioClick = onPlayAudioClick,
164+ onAddToPlaylistClick = onAddToPlaylistClick
165+ )
166+
167+ // Comments content (inline from CommentList)
168+ commentListContent(
102169 comments = commentsState.comments.itemList,
103170 isLoading = commentsState.common.isLoading,
104171 hasMoreComments = commentsState.comments.nextPageUrl != null ,
@@ -116,8 +183,8 @@ fun CommentSection(
116183 }
117184 }
118185 },
186+ showStickyHeader = false ,
119187 onBackClick = { },
120- listState = commentsListState,
121188 navController = navController,
122189 onTimestampClick = onTimestampClick
123190 )
0 commit comments