Moves to hold the feed itself in a stateflow
This commit is contained in:
@@ -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(
|
||||
|
||||
+7
-4
@@ -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,
|
||||
|
||||
+5
-11
@@ -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,
|
||||
|
||||
+16
-6
@@ -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,
|
||||
|
||||
+5
-3
@@ -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(
|
||||
|
||||
+5
-3
@@ -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()) {
|
||||
|
||||
+8
-4
@@ -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) {
|
||||
|
||||
+14
-21
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -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()
|
||||
|
||||
+7
-13
@@ -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,
|
||||
)
|
||||
|
||||
+21
-23
@@ -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,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user