Moves to hold the feed itself in a stateflow

This commit is contained in:
Vitor Pamplona
2024-08-26 17:48:27 -04:00
parent c88dded01b
commit 356740479c
14 changed files with 129 additions and 123 deletions
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.feeds
import android.util.Log
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.checkNotInMainThread
@@ -38,7 +37,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@Stable
@@ -79,7 +77,7 @@ class FeedContentState(
val oldNotesState = _feedContent.value
if (oldNotesState is FeedState.Loaded) {
if (!equalImmutableLists(notes, oldNotesState.feed.value)) {
if (!equalImmutableLists(notes, oldNotesState.feed.value.list)) {
updateFeed(notes)
}
} else {
@@ -88,21 +86,15 @@ class FeedContentState(
}
private fun updateFeed(notes: ImmutableList<Note>) {
viewModelScope.launch(Dispatchers.Main) {
val currentState = _feedContent.value
if (notes.isEmpty()) {
_feedContent.update { FeedState.Empty }
} else if (currentState is FeedState.Loaded) {
// updates the current list
if (currentState.showHidden.value != localFilter.showHiddenKey()) {
currentState.showHidden.value = localFilter.showHiddenKey()
}
currentState.feed.value = notes
} else {
_feedContent.update {
FeedState.Loaded(mutableStateOf(notes), mutableStateOf(localFilter.showHiddenKey()))
}
}
val currentState = _feedContent.value
if (notes.isEmpty()) {
_feedContent.tryEmit(FeedState.Empty)
} else if (currentState is FeedState.Loaded) {
currentState.feed.tryEmit(LoadedFeedState(notes, localFilter.showHiddenKey()))
} else {
_feedContent.tryEmit(
FeedState.Loaded(MutableStateFlow(LoadedFeedState(notes, localFilter.showHiddenKey()))),
)
}
}
@@ -118,11 +110,11 @@ class FeedContentState(
val oldList =
if (deletionEvents.isEmpty()) {
oldNotesState.feed.value
oldNotesState.feed.value.list
} else {
val deletedEventIds = deletionEvents.flatMapTo(HashSet()) { it.deleteEvents() }
val deletedEventAddresses = deletionEvents.flatMapTo(HashSet()) { it.deleteAddresses() }
oldNotesState.feed.value
oldNotesState.feed.value.list
.filter { !it.wasOrShouldBeDeletedBy(deletedEventIds, deletedEventAddresses) }
.toImmutableList()
}
@@ -132,7 +124,7 @@ class FeedContentState(
.updateListWith(oldList, newItems)
.distinctBy { it.idHex }
.toImmutableList()
if (!equalImmutableLists(newList, oldNotesState.feed.value)) {
if (!equalImmutableLists(newList, oldNotesState.feed.value.list)) {
updateFeed(newList)
}
} else if (oldNotesState is FeedState.Empty) {
@@ -28,7 +28,9 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
@@ -37,24 +39,26 @@ import com.vitorpamplona.amethyst.ui.theme.FeedPadding
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FeedLoaded(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
listState: LazyListState,
routeForLastRead: String?,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn(
contentPadding = FeedPadding,
state = listState,
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
Row(Modifier.fillMaxWidth().animateItemPlacement()) {
NoteCompose(
item,
modifier = Modifier.fillMaxWidth(),
routeForLastRead = routeForLastRead,
isBoostedNote = false,
isHiddenFeed = state.showHidden.value,
isHiddenFeed = items.showHidden,
quotesLeft = 3,
accountViewModel = accountViewModel,
nav = nav,
@@ -20,16 +20,17 @@
*/
package com.vitorpamplona.amethyst.ui.feeds
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.model.Note
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
@Stable
sealed class FeedState {
object Loading : FeedState()
class Loaded(
val feed: MutableState<ImmutableList<Note>>,
val showHidden: MutableState<Boolean>,
val feed: MutableStateFlow<LoadedFeedState<Note>>,
) : FeedState()
object Empty : FeedState()
@@ -38,3 +39,9 @@ sealed class FeedState {
val errorMessage: String,
) : FeedState()
}
@Stable
class LoadedFeedState<T>(
val list: ImmutableList<T>,
val showHidden: Boolean,
)
@@ -69,6 +69,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
@@ -224,22 +225,24 @@ fun ThreadFeedView(
@Composable
fun RenderThreadFeed(
noteId: String,
state: FeedState.Loaded,
loaded: FeedState.Loaded,
listState: LazyListState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
LaunchedEffect(noteId) {
// waits to load the thread to scroll to item.
delay(100)
val noteForPosition =
state.feed.value
items.list
.filter { it.idHex == noteId }
.firstOrNull()
var position = state.feed.value.indexOf(noteForPosition)
var position = items.list.indexOf(noteForPosition)
if (position >= 0) {
if (position >= 1 && position < state.feed.value.size - 1) {
if (position >= 1 && position < items.list.size - 1) {
position-- // show the replying note
}
@@ -251,7 +254,7 @@ fun RenderThreadFeed(
contentPadding = FeedPadding,
state = listState,
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { index, item ->
itemsIndexed(items.list, key = { _, item -> item.idHex }) { index, item ->
if (index == 0) {
ProvideTextStyle(TextStyle(fontSize = 18.sp, lineHeight = 1.20.em)) {
NoteMaster(
@@ -46,6 +46,7 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.components.SwipeToDeleteContainer
@@ -119,12 +120,14 @@ private fun RenderDraftListScreen(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun DraftFeedLoaded(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
listState: LazyListState,
routeForLastRead: String?,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
var showDeleteDialog by remember { mutableStateOf(false) }
if (showDeleteDialog) {
@@ -141,7 +144,7 @@ private fun DraftFeedLoaded(
confirmButton = {
TextButton(
onClick = {
accountViewModel.delete(state.feed.value)
accountViewModel.delete(items.list)
showDeleteDialog = false
},
) {
@@ -177,7 +180,7 @@ private fun DraftFeedLoaded(
}
}
}
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
Row(
Modifier
.fillMaxWidth()
@@ -196,7 +199,7 @@ private fun DraftFeedLoaded(
modifier = MaterialTheme.colorScheme.maxWidthWithBackground,
routeForLastRead = routeForLastRead,
isBoostedNote = false,
isHiddenFeed = state.showHidden.value,
isHiddenFeed = items.showHidden,
quotesLeft = 3,
accountViewModel = accountViewModel,
nav = nav,
@@ -40,7 +40,6 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment.Companion.BottomStart
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -118,26 +117,21 @@ fun RenderGalleryFeed(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun GalleryFeedLoaded(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
routeForLastRead: String?,
listState: LazyGridState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
LazyVerticalGrid(
columns = GridCells.Fixed(3),
contentPadding = FeedPadding,
state = listState,
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
val defaultModifier =
remember {
Modifier
.fillMaxWidth()
.animateItemPlacement()
}
Row(defaultModifier) {
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
Row(Modifier.fillMaxWidth().animateItemPlacement()) {
GalleryCardCompose(
baseNote = item,
routeForLastRead = routeForLastRead,
@@ -1238,12 +1238,7 @@ private fun DisplayAppRecommendations(
Column {
Text(stringRes(id = R.string.recommended_apps))
FlowRow(
verticalArrangement = Arrangement.Center,
modifier = Modifier.padding(vertical = 5.dp),
) {
state.feed.value.forEach { app -> WatchApp(app, nav) }
}
Recommends(state, nav)
}
}
else -> {}
@@ -1251,6 +1246,21 @@ private fun DisplayAppRecommendations(
}
}
@Composable
@OptIn(ExperimentalLayoutApi::class)
private fun Recommends(
loaded: FeedState.Loaded,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
FlowRow(
verticalArrangement = Arrangement.Center,
modifier = Modifier.padding(vertical = 5.dp),
) {
items.list.forEach { app -> WatchApp(app, nav) }
}
}
@Composable
private fun WatchApp(
baseApp: Note,
@@ -122,7 +122,7 @@ fun RenderChatroomFeedView(
@Composable
fun ChatroomFeedLoaded(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
accountViewModel: AccountViewModel,
listState: LazyListState,
nav: (String) -> Unit,
@@ -131,7 +131,9 @@ fun ChatroomFeedLoaded(
onWantsToEditDraft: (Note) -> Unit,
avoidDraft: String? = null,
) {
LaunchedEffect(state.feed.value.firstOrNull()) {
val items by loaded.feed.collectAsStateWithLifecycle()
LaunchedEffect(items.list.firstOrNull()) {
if (listState.firstVisibleItemIndex <= 1) {
listState.animateScrollToItem(0)
}
@@ -143,7 +145,7 @@ fun ChatroomFeedLoaded(
reverseLayout = true,
state = listState,
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
val noteEvent = item.event
if (avoidDraft == null || noteEvent !is DraftEvent || noteEvent.dTag() != avoidDraft) {
ChatroomMessageCompose(
@@ -88,16 +88,18 @@ private fun CrossFadeState(
@Composable
private fun FeedLoaded(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
markAsRead: MutableState<Boolean>,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
val listState = rememberLazyListState()
LaunchedEffect(key1 = markAsRead.value) {
if (markAsRead.value) {
accountViewModel.markAllAsRead(state.feed.value) { markAsRead.value = false }
accountViewModel.markAllAsRead(items.list) { markAsRead.value = false }
}
}
@@ -106,7 +108,7 @@ private fun FeedLoaded(
state = listState,
) {
itemsIndexed(
state.feed.value,
items.list,
key = { index, item -> if (index == 0) index else item.idHex },
) { _, item ->
Row(Modifier.fillMaxWidth()) {
@@ -347,18 +347,20 @@ fun WatchAccountForDiscoveryScreen(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun DiscoverFeedLoaded(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
routeForLastRead: String?,
listState: LazyListState,
forceEventKind: Int?,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn(
contentPadding = FeedPadding,
state = listState,
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
val defaultModifier = remember { Modifier.fillMaxWidth().animateItemPlacement() }
Row(defaultModifier) {
@@ -382,19 +384,21 @@ private fun DiscoverFeedLoaded(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun DiscoverFeedColumnsLoaded(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
routeForLastRead: String?,
listState: LazyGridState,
forceEventKind: Int?,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
LazyVerticalGrid(
columns = GridCells.Fixed(2),
contentPadding = FeedPadding,
state = listState,
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
val defaultModifier = remember { Modifier.fillMaxWidth().animateItemPlacement() }
Row(defaultModifier) {
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications
import android.util.Log
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -34,6 +33,7 @@ import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrderCard
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
import com.vitorpamplona.amethyst.ui.feeds.InvalidatableContent
import com.vitorpamplona.amethyst.ui.feeds.LoadedFeedState
import com.vitorpamplona.ammolite.relays.BundledInsert
import com.vitorpamplona.ammolite.relays.BundledUpdate
import com.vitorpamplona.quartz.events.BadgeAwardEvent
@@ -51,7 +51,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.time.Instant
import java.time.ZoneId
@@ -109,13 +108,13 @@ class CardFeedContentState(
lastAccount = (localFilter as? NotificationFeedFilter)?.account
val updatedCards =
(oldNotesState.feed.value + newCards)
(oldNotesState.feed.value.list + newCards)
.distinctBy { it.id() }
.sortedWith(DefaultFeedOrderCard)
.take(localFilter.limit())
.toImmutableList()
if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) {
if (!equalImmutableLists(oldNotesState.feed.value.list, updatedCards)) {
updateFeed(updatedCards)
}
}
@@ -291,21 +290,15 @@ class CardFeedContentState(
}
private fun updateFeed(notes: ImmutableList<Card>) {
viewModelScope.launch(Dispatchers.Main) {
val currentState = _feedContent.value
if (notes.isEmpty()) {
_feedContent.update { CardFeedState.Empty }
} else if (currentState is CardFeedState.Loaded) {
if (currentState.showHidden.value != localFilter.showHiddenKey()) {
currentState.showHidden.value = localFilter.showHiddenKey()
}
currentState.feed.value = notes
} else {
_feedContent.update {
CardFeedState.Loaded(mutableStateOf(notes), mutableStateOf(localFilter.showHiddenKey()))
}
}
val currentState = _feedContent.value
if (notes.isEmpty()) {
_feedContent.tryEmit(CardFeedState.Empty)
} else if (currentState is CardFeedState.Loaded) {
currentState.feed.tryEmit(LoadedFeedState(notes, localFilter.showHiddenKey()))
} else {
_feedContent.tryEmit(
CardFeedState.Loaded(MutableStateFlow(LoadedFeedState(notes, localFilter.showHiddenKey()))),
)
}
}
@@ -336,14 +329,14 @@ class CardFeedContentState(
lastAccount = (localFilter as? NotificationFeedFilter)?.account
val updatedCards =
(oldNotesState.feed.value + newCards)
(oldNotesState.feed.value.list + newCards)
.distinctBy { it.id() }
.sortedWith(compareBy({ it.createdAt() }, { it.id() }))
.reversed()
.take(localFilter.limit())
.toImmutableList()
if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) {
if (!equalImmutableLists(oldNotesState.feed.value.list, updatedCards)) {
updateFeed(updatedCards)
}
}
@@ -21,15 +21,16 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.firstFullCharOrEmoji
import com.vitorpamplona.amethyst.ui.feeds.LoadedFeedState
import com.vitorpamplona.quartz.events.ImmutableListOfLists
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.flow.MutableStateFlow
@Immutable
abstract class Card {
@@ -119,8 +120,7 @@ sealed class CardFeedState {
@Stable
class Loaded(
val feed: MutableState<ImmutableList<Card>>,
val showHidden: MutableState<Boolean>,
val feed: MutableStateFlow<LoadedFeedState<Card>>,
) : CardFeedState()
@Immutable object Empty : CardFeedState()
@@ -33,7 +33,6 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -115,7 +114,7 @@ fun RenderCardFeed(
}
is CardFeedState.Loaded -> {
FeedLoaded(
state = state,
loaded = state,
listState = listState,
routeForLastRead = routeForLastRead,
accountViewModel = accountViewModel,
@@ -132,12 +131,14 @@ fun RenderCardFeed(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun FeedLoaded(
state: CardFeedState.Loaded,
loaded: CardFeedState.Loaded,
listState: LazyListState,
routeForLastRead: String,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = FeedPadding,
@@ -148,22 +149,15 @@ private fun FeedLoaded(
}
itemsIndexed(
items = state.feed.value,
items = items.list,
key = { _, item -> item.id() },
contentType = { _, item -> item.javaClass.simpleName },
) { _, item ->
val defaultModifier =
remember {
Modifier
.fillMaxWidth()
.animateItemPlacement()
}
Row(defaultModifier) {
Row(Modifier.fillMaxWidth().animateItemPlacement()) {
RenderCardItem(
item,
routeForLastRead,
showHidden = state.showHidden.value,
showHidden = items.showHidden,
accountViewModel,
nav,
)
@@ -32,7 +32,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
@@ -42,7 +41,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -101,7 +99,6 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
import com.vitorpamplona.quartz.events.VideoEvent
import kotlinx.collections.immutable.ImmutableList
@Composable
fun VideoScreen(
@@ -183,28 +180,18 @@ fun RenderPage(
}
@Composable
@OptIn(ExperimentalFoundationApi::class)
private fun LoadedState(
state: FeedState.Loaded,
loaded: FeedState.Loaded,
pagerStateKey: String?,
videoFeedContentState: FeedContentState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val pagerState =
if (pagerStateKey != null) {
rememberForeverPagerState(pagerStateKey) { state.feed.value.size }
} else {
rememberPagerState { state.feed.value.size }
}
WatchScrollToTop(videoFeedContentState, pagerState)
RefresheableBox(invalidateableContent = videoFeedContentState) {
SlidingCarousel(
state.feed,
pagerState,
state.showHidden.value,
loaded,
pagerStateKey,
videoFeedContentState,
accountViewModel,
nav,
)
@@ -214,25 +201,36 @@ private fun LoadedState(
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SlidingCarousel(
feed: MutableState<ImmutableList<Note>>,
pagerState: PagerState,
showHidden: Boolean,
loaded: FeedState.Loaded,
pagerStateKey: String?,
videoFeedContentState: FeedContentState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
val pagerState =
if (pagerStateKey != null) {
rememberForeverPagerState(pagerStateKey, items.list.size) { items.list.size }
} else {
rememberPagerState(items.list.size) { items.list.size }
}
WatchScrollToTop(videoFeedContentState, pagerState)
VerticalPager(
state = pagerState,
beyondBoundsPageCount = 1,
modifier = Modifier.fillMaxSize(),
key = { index -> feed.value.getOrNull(index)?.idHex ?: "$index" },
key = { index -> items.list.getOrNull(index)?.idHex ?: "$index" },
) { index ->
feed.value.getOrNull(index)?.let { note ->
items.list.getOrNull(index)?.let { note ->
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CheckHiddenFeedWatchBlockAndReport(
note = note,
modifier = Modifier.fillMaxWidth(),
showHiddenWarning = true,
ignoreAllBlocksAndReports = showHidden,
ignoreAllBlocksAndReports = items.showHidden,
accountViewModel = accountViewModel,
nav = nav,
) {