- Redesign feed invalidations to account for changes in the follow list

- Redesign scroll to the top implementation to avoid using arguments in the navigator.
This commit is contained in:
Vitor Pamplona
2023-06-19 14:30:15 -04:00
parent be8848fb1b
commit 58c93e0cce
37 changed files with 275 additions and 150 deletions
@@ -7,6 +7,10 @@ import com.vitorpamplona.amethyst.model.Note
object BookmarkPrivateFeedFilter : FeedFilter<Note>() {
lateinit var account: Account
override fun feedKey(): String {
return account.userProfile().latestBookmarkList?.id ?: ""
}
override fun feed(): List<Note> {
val privKey = account.loggedIn.privKey ?: return emptyList()
@@ -7,6 +7,9 @@ import com.vitorpamplona.amethyst.model.Note
object BookmarkPublicFeedFilter : FeedFilter<Note>() {
lateinit var account: Account
override fun feedKey(): String {
return BookmarkPrivateFeedFilter.account.userProfile().latestBookmarkList?.id ?: ""
}
override fun feed(): List<Note> {
val bookmarks = account.userProfile().latestBookmarkList
@@ -5,6 +5,11 @@ import com.vitorpamplona.amethyst.model.Channel
import com.vitorpamplona.amethyst.model.Note
class ChannelFeedFilter(val channel: Channel, val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return channel.idHex
}
// returns the last Note of each user.
override fun feed(): List<Note> {
return channel.notes
@@ -6,6 +6,10 @@ import com.vitorpamplona.amethyst.model.User
class ChatroomFeedFilter(val withUser: User, val account: Account) : AdditiveFeedFilter<Note>() {
// returns the last Note of each user.
override fun feedKey(): String {
return withUser.pubkeyHex
}
override fun feed(): List<Note> {
val messages = account
.userProfile()
@@ -11,6 +11,10 @@ import kotlin.time.measureTimedValue
class ChatroomListKnownFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex
}
// returns the last Note of each user.
override fun feed(): List<Note> {
val me = account.userProfile()
@@ -10,6 +10,10 @@ import kotlin.time.measureTimedValue
class ChatroomListNewFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex
}
// returns the last Note of each user.
override fun feed(): List<Note> {
val me = account.userProfile()
@@ -18,6 +18,11 @@ abstract class FeedFilter<T> {
return feed.take(1000)
}
/**
* Returns a string that serves as the key to invalidate the list if it changes.
*/
abstract fun feedKey(): String
abstract fun feed(): List<T>
}
@@ -7,6 +7,10 @@ import com.vitorpamplona.amethyst.service.model.*
class GlobalFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex
}
override fun feed(): List<Note> {
val notes = innerApplyFilter(LocalCache.notes.values)
val longFormNotes = innerApplyFilter(LocalCache.addressables.values)
@@ -12,6 +12,10 @@ object HashtagFeedFilter : AdditiveFeedFilter<Note>() {
lateinit var account: Account
var tag: String? = null
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + tag
}
fun loadHashtag(account: Account, tag: String?) {
this.account = account
this.tag = tag
@@ -5,6 +5,9 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
class HiddenAccountsFeedFilter(val account: Account) : FeedFilter<User>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + HashtagFeedFilter.tag
}
override fun feed(): List<User> {
return (account.hiddenUsers + account.transientHiddenUsers)
@@ -7,6 +7,11 @@ import com.vitorpamplona.amethyst.service.model.PollNoteEvent
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + account.defaultHomeFollowList
}
override fun feed(): List<Note> {
return sort(innerApplyFilter(LocalCache.notes.values))
}
@@ -12,6 +12,10 @@ import com.vitorpamplona.amethyst.service.model.RepostEvent
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + account.defaultHomeFollowList
}
override fun feed(): List<Note> {
val notes = innerApplyFilter(LocalCache.notes.values)
val longFormNotes = innerApplyFilter(LocalCache.addressables.values)
@@ -8,6 +8,10 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.model.*
class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + account.defaultNotificationFollowList
}
override fun feed(): List<Note> {
return sort(innerApplyFilter(LocalCache.notes.values))
}
@@ -8,6 +8,10 @@ import com.vitorpamplona.amethyst.service.model.PeopleListEvent
object PeopleListFeedFilter : FeedFilter<Note>() {
lateinit var account: Account
override fun feedKey(): String {
return account.userProfile().pubkeyHex
}
override fun feed(): List<Note> {
val privKey = account.loggedIn.privKey ?: return emptyList()
@@ -7,6 +7,10 @@ import com.vitorpamplona.amethyst.model.ThreadAssembler
@Immutable
class ThreadFeedFilter(val noteId: String) : FeedFilter<Note>() {
override fun feedKey(): String {
return noteId
}
override fun feed(): List<Note> {
val cachedSignatures: MutableMap<Note, String> = mutableMapOf()
val eventsToWatch = ThreadAssembler().findThreadFor(noteId) ?: emptySet()
@@ -6,6 +6,10 @@ import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.model.AppRecommendationEvent
class UserProfileAppRecommendationsFeedFilter(val user: User) : FeedFilter<Note>() {
override fun feedKey(): String {
return user.pubkeyHex
}
override fun feed(): List<Note> {
val recommendations = LocalCache.addressables.values.filter {
(it.event as? AppRecommendationEvent)?.pubKey == user.pubkeyHex
@@ -9,6 +9,10 @@ object UserProfileBookmarksFeedFilter : FeedFilter<Note>() {
lateinit var account: Account
var user: User? = null
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + user?.pubkeyHex
}
fun loadUserProfile(accountLoggedIn: Account, user: User?) {
account = accountLoggedIn
this.user = user
@@ -8,6 +8,10 @@ object UserProfileConversationsFeedFilter : FeedFilter<Note>() {
var account: Account? = null
var user: User? = null
override fun feedKey(): String {
return account?.userProfile()?.pubkeyHex + "-" + user?.pubkeyHex
}
fun loadUserProfile(accountLoggedIn: Account, user: User?) {
account = accountLoggedIn
this.user = user
@@ -6,6 +6,9 @@ import com.vitorpamplona.amethyst.model.User
class UserProfileFollowersFeedFilter(val user: User, val account: Account) : FeedFilter<User>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + user.pubkeyHex
}
override fun feed(): List<User> {
return LocalCache.users.values.filter { it.isFollowing(user) && !account.isHidden(it) }
}
@@ -7,6 +7,10 @@ import com.vitorpamplona.amethyst.service.model.ContactListEvent
class UserProfileFollowsFeedFilter(val user: User, val account: Account) : FeedFilter<User>() {
override fun feedKey(): String {
return account.userProfile()?.pubkeyHex + "-" + user.pubkeyHex
}
val cache: MutableMap<ContactListEvent, List<User>> = mutableMapOf()
override fun feed(): List<User> {
@@ -12,6 +12,10 @@ object UserProfileNewThreadFeedFilter : FeedFilter<Note>() {
var account: Account? = null
var user: User? = null
override fun feedKey(): String {
return account?.userProfile()?.pubkeyHex + "-" + user?.pubkeyHex
}
fun loadUserProfile(accountLoggedIn: Account, user: User) {
account = accountLoggedIn
this.user = user
@@ -8,6 +8,10 @@ import com.vitorpamplona.amethyst.service.model.ReportEvent
object UserProfileReportsFeedFilter : FeedFilter<Note>() {
var user: User? = null
override fun feedKey(): String {
return user?.pubkeyHex ?: ""
}
fun loadUserProfile(user: User?) {
this.user = user
}
@@ -5,6 +5,11 @@ import com.vitorpamplona.amethyst.service.model.zaps.UserZaps
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
class UserProfileZapsFeedFilter(val user: User) : FeedFilter<ZapReqResponse>() {
override fun feedKey(): String {
return user.pubkeyHex
}
override fun feed(): List<ZapReqResponse> {
return UserZaps.forProfileFeed(user.zaps)
}
@@ -7,6 +7,10 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.model.*
class VideoFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + account.defaultStoriesFollowList
}
override fun feed(): List<Note> {
val notes = innerApplyFilter(LocalCache.notes.values)
@@ -60,51 +60,10 @@ fun AppNavigation(
}
NavHost(navController, startDestination = Route.Home.route) {
Route.Video.let { route ->
composable(route.route, route.arguments, content = {
val scrollToTop = it.arguments?.getBoolean("scrollToTop") ?: false
if (scrollToTop) {
videoFeedViewModel.sendToTop()
it.arguments?.remove("scrollToTop")
}
VideoScreen(
videoFeedView = videoFeedViewModel,
accountViewModel = accountViewModel,
nav = nav
)
})
}
Route.Search.let { route ->
composable(route.route, route.arguments, content = {
val scrollToTop = it.arguments?.getBoolean("scrollToTop") ?: false
if (scrollToTop) {
searchFeedViewModel.sendToTop()
it.arguments?.remove("scrollToTop")
}
SearchScreen(
searchFeedViewModel = searchFeedViewModel,
accountViewModel = accountViewModel,
nav = nav
)
})
}
Route.Home.let { route ->
composable(route.route, route.arguments, content = { it ->
val scrollToTop = it.arguments?.getBoolean("scrollToTop") ?: false
val nip47 = it.arguments?.getString("nip47")
if (scrollToTop) {
homeFeedViewModel.sendToTop()
repliesFeedViewModel.sendToTop()
it.arguments?.remove("scrollToTop")
}
HomeScreen(
homeFeedViewModel = homeFeedViewModel,
repliesFeedViewModel = repliesFeedViewModel,
@@ -124,25 +83,6 @@ fun AppNavigation(
})
}
Route.Notification.let { route ->
composable(route.route, route.arguments, content = {
val scrollToTop = it.arguments?.getBoolean("scrollToTop") ?: false
if (scrollToTop) {
notifFeedViewModel.clear()
notifFeedViewModel.invalidateDataAndSendToTop()
it.arguments?.remove("scrollToTop")
}
NotificationScreen(
notifFeedViewModel = notifFeedViewModel,
userReactionsStatsModel = userReactionsStatsModel,
accountViewModel = accountViewModel,
nav = nav
)
})
}
composable(
Route.Message.route,
content = {
@@ -155,6 +95,37 @@ fun AppNavigation(
}
)
Route.Video.let { route ->
composable(route.route, route.arguments, content = {
VideoScreen(
videoFeedView = videoFeedViewModel,
accountViewModel = accountViewModel,
nav = nav
)
})
}
Route.Search.let { route ->
composable(route.route, route.arguments, content = {
SearchScreen(
searchFeedViewModel = searchFeedViewModel,
accountViewModel = accountViewModel,
nav = nav
)
})
}
Route.Notification.let { route ->
composable(route.route, route.arguments, content = {
NotificationScreen(
notifFeedViewModel = notifFeedViewModel,
userReactionsStatsModel = userReactionsStatsModel,
accountViewModel = accountViewModel,
nav = nav
)
})
}
composable(Route.BlockedUsers.route, content = { HiddenUsersScreen(accountViewModel, nav) })
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, nav) })
@@ -33,31 +33,27 @@ sealed class Route(
get() = route.substringBefore("?")
object Home : Route(
route = "Home?scrollToTop={scrollToTop}&nip47={nip47}",
route = "Home?nip47={nip47}",
icon = R.drawable.ic_home,
arguments = listOf(
navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false },
navArgument("nip47") { type = NavType.StringType; nullable = true; defaultValue = null }
).toImmutableList(),
hasNewItems = { accountViewModel, newNotes -> HomeLatestItem.hasNewItems(accountViewModel, newNotes) }
)
object Search : Route(
route = "Search?scrollToTop={scrollToTop}",
icon = R.drawable.ic_globe,
arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }).toImmutableList()
route = "Search",
icon = R.drawable.ic_globe
)
object Video : Route(
route = "Video?scrollToTop={scrollToTop}",
icon = R.drawable.ic_video,
arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }).toImmutableList()
route = "Video",
icon = R.drawable.ic_video
)
object Notification : Route(
route = "Notification?scrollToTop={scrollToTop}",
route = "Notification",
icon = R.drawable.ic_notifications,
arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }).toImmutableList(),
hasNewItems = { accountViewModel, newNotes -> NotificationLatestItem.hasNewItems(accountViewModel, newNotes) }
)
@@ -56,6 +56,8 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
val scrollToTop = _scrollToTop.asStateFlow()
var scrolltoTopPending = false
private var lastFeedKey: String? = null
fun sendToTop() {
if (scrolltoTopPending) return
@@ -84,6 +86,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
checkNotInMainThread()
val notes = localFilter.feed()
lastFeedKey = localFilter.feedKey()
val thisAccount = (localFilter as? NotificationFeedFilter)?.account
val lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null
@@ -233,6 +236,8 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
val lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null
if (lastNotesCopy != null && localFilter is AdditiveFeedFilter && oldNotesState is CardFeedState.Loaded) {
lastFeedKey = localFilter.feedKey()
val filteredNewList = localFilter.applyFilter(newItems)
if (filteredNewList.isEmpty()) return
@@ -279,15 +284,18 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
}
@OptIn(ExperimentalTime::class)
fun invalidateDataAndSendToTop(ignoreIfDoing: Boolean = false) {
bundler.invalidate(ignoreIfDoing) {
// adds the time to perform the refresh into this delay
// holding off new updates in case of heavy refresh routines.
val (value, elapsed) = measureTimedValue {
refreshSuspended()
sendToTop()
fun checkKeysInvalidateDataAndSendToTop() {
if (lastFeedKey != localFilter.feedKey()) {
clear()
bundler.invalidate(false) {
// adds the time to perform the refresh into this delay
// holding off new updates in case of heavy refresh routines.
val (value, elapsed) = measureTimedValue {
refreshSuspended()
sendToTop()
}
Log.d("Time", "${this.javaClass.simpleName} Card update $elapsed")
}
Log.d("Time", "${this.javaClass.simpleName} Card update $elapsed")
}
}
@@ -40,14 +40,13 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
fun RefresheableFeedView(
viewModel: FeedViewModel,
routeForLastRead: String?,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
enablePullRefresh: Boolean = true,
scrollStateKey: String? = null,
enablePullRefresh: Boolean = true
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
RefresheableView(viewModel, enablePullRefresh) {
SaveableFeedState(viewModel, accountViewModel, nav, routeForLastRead, scrollStateKey)
SaveableFeedState(viewModel, routeForLastRead, scrollStateKey, accountViewModel, nav)
}
}
@@ -82,10 +81,10 @@ fun RefresheableView(
@Composable
private fun SaveableFeedState(
viewModel: FeedViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
routeForLastRead: String?,
scrollStateKey: String? = null
scrollStateKey: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
SaveableFeedState(viewModel, scrollStateKey) { listState ->
RenderFeed(viewModel, accountViewModel, listState, nav, routeForLastRead)
@@ -138,6 +138,8 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
val scrollToTop = _scrollToTop.asStateFlow()
var scrolltoTopPending = false
private var lastFeedKey: String? = null
fun sendToTop() {
if (scrolltoTopPending) return
@@ -151,10 +153,6 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
scrolltoTopPending = false
}
fun newListFromDataSource(): ImmutableList<Note> {
return localFilter.loadTop().toImmutableList()
}
private fun refresh() {
val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch {
@@ -165,7 +163,8 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
fun refreshSuspended() {
checkNotInMainThread()
val notes = newListFromDataSource()
val notes = localFilter.loadTop().toImmutableList()
lastFeedKey = localFilter.feedKey()
val oldNotesState = _feedContent.value
if (oldNotesState is FeedState.Loaded) {
@@ -197,11 +196,13 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
if (localFilter is AdditiveFeedFilter) {
if (oldNotesState is FeedState.Loaded) {
val newList = localFilter.updateListWith(oldNotesState.feed.value, newItems.toSet()).toImmutableList()
lastFeedKey = localFilter.feedKey()
if (!equalImmutableLists(newList, oldNotesState.feed.value)) {
updateFeed(newList)
}
} else if (oldNotesState is FeedState.Empty) {
val newList = localFilter.updateListWith(emptyList(), newItems.toSet()).toImmutableList()
lastFeedKey = localFilter.feedKey()
if (newList.isNotEmpty()) {
updateFeed(newList)
}
@@ -226,12 +227,14 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
}
}
fun invalidateDataAndSendToTop(ignoreIfDoing: Boolean = false) {
bundler.invalidate(ignoreIfDoing) {
// adds the time to perform the refresh into this delay
// holding off new updates in case of heavy refresh routines.
refreshSuspended()
sendToTop()
fun checkKeysInvalidateDataAndSendToTop() {
if (lastFeedKey != localFilter.feedKey()) {
bundler.invalidate(false) {
// adds the time to perform the refresh into this delay
// holding off new updates in case of heavy refresh routines.
refreshSuspended()
sendToTop()
}
}
}
@@ -73,8 +73,18 @@ fun BookmarkListScreen(accountViewModel: AccountViewModel, nav: (String) -> Unit
}
HorizontalPager(pageCount = 2, state = pagerState) { page ->
when (page) {
0 -> RefresheableFeedView(privateFeedViewModel, null, accountViewModel, nav)
1 -> RefresheableFeedView(publicFeedViewModel, null, accountViewModel, nav)
0 -> RefresheableFeedView(
privateFeedViewModel,
null,
accountViewModel = accountViewModel,
nav = nav
)
1 -> RefresheableFeedView(
publicFeedViewModel,
null,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
@@ -73,7 +73,12 @@ fun HashtagScreen(tag: String?, accountViewModel: AccountViewModel, nav: (String
modifier = Modifier.padding(vertical = 0.dp)
) {
HashtagHeader(tag, accountViewModel)
RefresheableFeedView(feedViewModel, null, accountViewModel, nav)
RefresheableFeedView(
feedViewModel,
null,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
@@ -123,10 +123,10 @@ private fun HomePages(
HorizontalPager(pageCount = 2, state = pagerState) { page ->
RefresheableFeedView(
viewModel = tabs[page].viewModel,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = tabs[page].routeForLastRead,
scrollStateKey = tabs[page].scrollStateKey
scrollStateKey = tabs[page].scrollStateKey,
accountViewModel = accountViewModel,
nav = nav
)
}
}
@@ -143,16 +143,14 @@ fun WatchAccountForHomeScreen(
LaunchedEffect(accountViewModel, accountState?.account?.defaultHomeFollowList) {
launch(Dispatchers.IO) {
NostrHomeDataSource.invalidateFilters()
homeFeedViewModel.invalidateDataAndSendToTop(true)
repliesFeedViewModel.invalidateDataAndSendToTop(true)
homeFeedViewModel.checkKeysInvalidateDataAndSendToTop()
repliesFeedViewModel.checkKeysInvalidateDataAndSendToTop()
}
}
LaunchedEffect(followState) {
launch(Dispatchers.IO) {
NostrHomeDataSource.invalidateFilters()
homeFeedViewModel.invalidateData(true)
repliesFeedViewModel.invalidateData(true)
}
}
}
@@ -73,23 +73,6 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
}
}
val navBottomRow = remember(navController) {
{ route: Route, selected: Boolean ->
if (!selected) {
navController.navigate(route.base) {
popUpTo(Route.Home.route)
launchSingleTop = true
}
} else {
val newRoute = route.route.replace("{scrollToTop}", "true")
navController.navigate(newRoute) {
popUpTo(Route.Home.route)
launchSingleTop = true
}
}
}
}
val followLists: FollowListViewModel = viewModel(
key = accountViewModel.userProfile().pubkeyHex + "FollowListViewModel",
factory = FollowListViewModel.Factory(accountViewModel.account)
@@ -136,6 +119,41 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
factory = NostrChatroomListNewFeedViewModel.Factory(accountViewModel.account)
)
val navBottomRow = remember(navController) {
{ route: Route, selected: Boolean ->
if (!selected) {
navController.navigate(route.base) {
popUpTo(Route.Home.route)
launchSingleTop = true
}
} else {
// deals with scroll to top here to avoid passing as parameter
// and having to deal with all recompositions with scroll to top true
when (route.base) {
Route.Home.base -> {
homeFeedViewModel.sendToTop()
repliesFeedViewModel.sendToTop()
}
Route.Search.base -> {
searchFeedViewModel.sendToTop()
}
Route.Video.base -> {
videoFeedViewModel.sendToTop()
}
Route.Notification.base -> {
notifFeedViewModel.clear()
notifFeedViewModel.sendToTop()
}
}
navController.navigate(route.route) {
popUpTo(route.route)
launchSingleTop = true
}
}
}
}
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
@@ -102,16 +102,9 @@ fun WatchAccountForNotifications(
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
var firstTime by remember(accountViewModel) { mutableStateOf(true) }
LaunchedEffect(accountViewModel, accountState?.account?.defaultNotificationFollowList) {
if (firstTime) {
firstTime = false
} else {
NostrAccountDataSource.invalidateFilters()
notifFeedViewModel.clear()
notifFeedViewModel.invalidateDataAndSendToTop(true)
}
NostrAccountDataSource.invalidateFilters()
notifFeedViewModel.checkKeysInvalidateDataAndSendToTop()
}
}
@@ -1130,7 +1130,13 @@ fun TabNotesNewThreads(accountViewModel: AccountViewModel, nav: (String) -> Unit
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
RefresheableFeedView(feedViewModel, null, accountViewModel, nav, enablePullRefresh = false)
RefresheableFeedView(
feedViewModel,
null,
enablePullRefresh = false,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
@@ -1147,7 +1153,13 @@ fun TabNotesConversations(accountViewModel: AccountViewModel, nav: (String) -> U
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
RefresheableFeedView(feedViewModel, null, accountViewModel, nav, enablePullRefresh = false)
RefresheableFeedView(
feedViewModel,
null,
enablePullRefresh = false,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
@@ -1164,7 +1176,13 @@ fun TabBookmarks(baseUser: User, accountViewModel: AccountViewModel, nav: (Strin
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
RefresheableFeedView(feedViewModel, null, accountViewModel, nav, enablePullRefresh = false)
RefresheableFeedView(
feedViewModel,
null,
enablePullRefresh = false,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
@@ -1246,7 +1264,13 @@ fun TabReports(baseUser: User, accountViewModel: AccountViewModel, nav: (String)
Column(Modifier.fillMaxHeight()) {
Column() {
RefresheableFeedView(feedViewModel, null, accountViewModel, nav, enablePullRefresh = false)
RefresheableFeedView(
feedViewModel,
null,
enablePullRefresh = false,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
@@ -138,7 +138,13 @@ fun SearchScreen(
modifier = Modifier.padding(vertical = 0.dp)
) {
SearchBar(searchBarViewModel, accountViewModel, nav)
RefresheableFeedView(searchFeedViewModel, null, accountViewModel, nav, ScrollStateKeys.GLOBAL_SCREEN)
RefresheableFeedView(
searchFeedViewModel,
null,
scrollStateKey = ScrollStateKeys.GLOBAL_SCREEN,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
@@ -127,7 +127,12 @@ fun VideoScreen(
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
SaveableFeedState(videoFeedView, accountViewModel, nav, ScrollStateKeys.VIDEO_SCREEN)
SaveableFeedState(
videoFeedView = videoFeedView,
accountViewModel = accountViewModel,
nav = nav,
scrollStateKey = ScrollStateKeys.VIDEO_SCREEN
)
}
}
}
@@ -135,17 +140,10 @@ fun VideoScreen(
@Composable
fun WatchAccountForVideoScreen(videoFeedView: NostrVideoFeedViewModel, accountViewModel: AccountViewModel) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account } ?: return
var firstTime by remember(accountViewModel) { mutableStateOf(true) }
LaunchedEffect(accountViewModel, account.defaultStoriesFollowList) {
if (firstTime) {
firstTime = false
} else {
NostrVideoDataSource.resetFilters()
videoFeedView.invalidateDataAndSendToTop(true)
}
LaunchedEffect(accountViewModel, accountState?.account?.defaultStoriesFollowList) {
NostrVideoDataSource.resetFilters()
videoFeedView.checkKeysInvalidateDataAndSendToTop()
}
}
@@ -155,7 +153,6 @@ private fun SaveableFeedState(
videoFeedView: NostrVideoFeedViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
routeForLastRead: String?,
scrollStateKey: String? = null
) {
val pagerState = if (scrollStateKey != null) {