Saves the position of the Notification feed

This commit is contained in:
Vitor Pamplona
2023-03-30 13:58:28 -04:00
parent eff07bfb1e
commit b4e409db4d
7 changed files with 110 additions and 16 deletions

View File

@@ -53,13 +53,9 @@ fun AppNavigation(
GlobalFeedFilter.account = account
val searchFeedViewModel: NostrGlobalFeedViewModel = viewModel()
val restartNotificationList = NotificationFeedFilter.isDifferentAccount(account)
NotificationFeedFilter.account = account
val notifFeedViewModel: NotificationViewModel = viewModel()
if (restartNotificationList) notifFeedViewModel.clear()
NavHost(navController, startDestination = Route.Home.route) {
Route.Search.let { route ->
composable(route.route, route.arguments, content = {
@@ -99,8 +95,25 @@ fun AppNavigation(
})
}
Route.Notification.let { route ->
composable(route.route, route.arguments, content = {
val scrollToTop = it.arguments?.getBoolean("scrollToTop") ?: false
NotificationScreen(
notifFeedViewModel = notifFeedViewModel,
accountViewModel = accountViewModel,
navController = navController,
scrollToTop = scrollToTop
)
// Avoids running scroll to top when back button is pressed
if (scrollToTop) {
it.arguments?.remove("scrollToTop")
}
})
}
composable(Route.Message.route, content = { ChatroomListScreen(accountViewModel, navController) })
composable(Route.Notification.route, content = { NotificationScreen(notifFeedViewModel, accountViewModel, navController) })
composable(Route.BlockedUsers.route, content = { HiddenUsersScreen(accountViewModel, navController) })
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, navController) })

View File

@@ -37,8 +37,9 @@ sealed class Route(
)
object Notification : Route(
route = "Notification",
route = "Notification?scrollToTop={scrollToTop}",
icon = R.drawable.ic_notifications,
arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }),
hasNewItems = { accountViewModel, cache -> notificationHasNewItems(accountViewModel, cache) }
)

View File

@@ -13,6 +13,7 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -33,7 +34,14 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CardFeedView(viewModel: CardFeedViewModel, accountViewModel: AccountViewModel, navController: NavController, routeForLastRead: String) {
fun CardFeedView(
viewModel: CardFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
routeForLastRead: String,
scrollStateKey: String? = null,
scrollToTop: Boolean = false
) {
val feedState by viewModel.feedContent.collectAsState()
var refreshing by remember { mutableStateOf(false) }
@@ -57,10 +65,12 @@ fun CardFeedView(viewModel: CardFeedViewModel, accountViewModel: AccountViewMode
is CardFeedState.Loaded -> {
refreshing = false
FeedLoaded(
state,
accountViewModel,
navController,
routeForLastRead
state = state,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead,
scrollStateKey = scrollStateKey,
scrollToTop = scrollToTop
)
}
CardFeedState.Loading -> {
@@ -79,9 +89,21 @@ private fun FeedLoaded(
state: CardFeedState.Loaded,
accountViewModel: AccountViewModel,
navController: NavController,
routeForLastRead: String
routeForLastRead: String,
scrollStateKey: String?,
scrollToTop: Boolean = false
) {
val listState = rememberLazyListState()
val listState = if (scrollStateKey != null) {
rememberForeverLazyListState(scrollStateKey)
} else {
rememberLazyListState()
}
if (scrollToTop) {
LaunchedEffect(Unit) {
listState.scrollToItem(index = 0)
}
}
LazyColumn(
contentPadding = PaddingValues(

View File

@@ -3,6 +3,7 @@ package com.vitorpamplona.amethyst.ui.screen
import android.util.Log
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.LocalCacheState
import com.vitorpamplona.amethyst.model.Note
@@ -32,6 +33,7 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading)
val feedContent = _feedContent.asStateFlow()
private var lastAccount: Account? = null
private var lastNotes: List<Note>? = null
private fun refresh() {
@@ -45,18 +47,21 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
private fun refreshSuspended() {
val notes = dataSource.loadTop()
val lastNotesCopy = lastNotes
val thisAccount = (dataSource as? NotificationFeedFilter)?.account
val lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null
val oldNotesState = feedContent.value
if (lastNotesCopy != null && oldNotesState is CardFeedState.Loaded) {
val newCards = convertToCard(notes.minus(lastNotesCopy))
if (newCards.isNotEmpty()) {
lastNotes = notes
lastAccount = (dataSource as? NotificationFeedFilter)?.account
updateFeed((oldNotesState.feed.value + newCards).distinctBy { it.id() }.sortedBy { it.createdAt() }.reversed())
}
} else {
val cards = convertToCard(notes)
lastNotes = notes
lastAccount = (dataSource as? NotificationFeedFilter)?.account
updateFeed(cards)
}
}

View File

@@ -11,6 +11,7 @@ private data class ScrollState(val index: Int, val scrollOffset: Int)
object ScrollStateKeys {
const val GLOBAL_SCREEN = "Global"
const val NOTIFICATION_SCREEN = "Notifications"
val HOME_FOLLOWS = Route.Home.base + "Follows"
val HOME_REPLIES = Route.Home.base + "FollowsReplies"
}

View File

@@ -18,9 +18,15 @@ import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.screen.CardFeedView
import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys
@Composable
fun NotificationScreen(notifFeedViewModel: NotificationViewModel, accountViewModel: AccountViewModel, navController: NavController) {
fun NotificationScreen(
notifFeedViewModel: NotificationViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
scrollToTop: Boolean = false
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -48,7 +54,14 @@ fun NotificationScreen(notifFeedViewModel: NotificationViewModel, accountViewMod
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
CardFeedView(notifFeedViewModel, accountViewModel = accountViewModel, navController, Route.Notification.route)
CardFeedView(
viewModel = notifFeedViewModel,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = Route.Notification.base,
scrollStateKey = ScrollStateKeys.NOTIFICATION_SCREEN,
scrollToTop = scrollToTop
)
}
}
}

View File

@@ -0,0 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="250dp"
android:height="250dp"
android:viewportWidth="250"
android:viewportHeight="250">
<path
android:pathData="M48.12,70.63m18.75,0a18.75,18.75 0,1 0,-37.5 0a18.75,18.75 0,1 0,37.5 0"
android:fillColor="#000000"/>
<path
android:pathData="M45,67.82L80,102.82"
android:strokeWidth="9.37484"
android:fillColor="#00000000"
android:strokeColor="#000000"/>
<path
android:pathData="M201.25,70.63m-18.75,0a18.75,18.75 0,1 1,37.5 0a18.75,18.75 0,1 1,-37.5 0"
android:fillColor="#000000"/>
<path
android:pathData="M204.68,67.82L169.68,102.82"
android:strokeWidth="9.37484"
android:fillColor="#00000000"
android:strokeColor="#000000"/>
<path
android:pathData="M53.45,182.51C43.34,177.7 37.46,166.9 39.43,155.88C47.89,108.59 83.12,73.13 125.31,73.13C167.6,73.13 202.91,108.77 211.25,156.23C213.19,167.26 207.26,178.06 197.12,182.83C175.43,193.04 151.19,198.75 125.62,198.75C99.79,198.75 75.32,192.92 53.45,182.51Z"
android:fillColor="#FFDF6F"
android:fillType="evenOdd"/>
<path
android:pathData="M211.25,156.23L206.64,157.04L211.25,156.23ZM53.45,182.51L51.43,186.74L53.45,182.51ZM44.04,156.71C52.21,111.01 85.99,77.82 125.31,77.82V68.44C80.26,68.44 43.56,106.17 34.82,155.06L44.04,156.71ZM125.31,77.82C164.73,77.82 198.57,111.18 206.64,157.04L215.87,155.41C207.24,106.36 170.47,68.44 125.31,68.44V77.82ZM195.13,178.59C174.04,188.51 150.49,194.06 125.62,194.06V203.44C151.9,203.44 176.81,197.57 199.12,187.07L195.13,178.59ZM125.62,194.06C100.5,194.06 76.71,188.4 55.46,178.28L51.43,186.74C73.92,197.45 99.08,203.44 125.62,203.44V194.06ZM206.64,157.04C208.19,165.89 203.45,174.67 195.13,178.59L199.12,187.07C211.07,181.45 218.2,168.64 215.87,155.41L206.64,157.04ZM34.82,155.06C32.45,168.26 39.52,181.07 51.43,186.74L55.46,178.28C47.16,174.33 42.46,165.54 44.04,156.71L34.82,155.06Z"
android:fillColor="#000000"/>
<path
android:pathData="M71.48,170.76C63.34,167.44 58.52,158.79 61.34,150.47C70.03,124.82 95.38,106.25 125.31,106.25C155.24,106.25 180.59,124.82 189.29,150.47C192.1,158.79 187.28,167.44 179.14,170.76C162.53,177.52 144.36,181.25 125.31,181.25C106.26,181.25 88.09,177.52 71.48,170.76Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M131.25,146.25a15.62,12.5 0,1 0,31.25 0a15.62,12.5 0,1 0,-31.25 0z"
android:fillColor="#ffffff"/>
<path
android:pathData="M86.52,146.26a15.62,12.5 0,1 0,31.25 0a15.62,12.5 0,1 0,-31.25 0z"
android:fillColor="#ffffff"/>
</vector>