mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-26 01:56:36 +02:00
Saves the position of the Notification feed
This commit is contained in:
@@ -53,13 +53,9 @@ fun AppNavigation(
|
|||||||
GlobalFeedFilter.account = account
|
GlobalFeedFilter.account = account
|
||||||
val searchFeedViewModel: NostrGlobalFeedViewModel = viewModel()
|
val searchFeedViewModel: NostrGlobalFeedViewModel = viewModel()
|
||||||
|
|
||||||
val restartNotificationList = NotificationFeedFilter.isDifferentAccount(account)
|
|
||||||
|
|
||||||
NotificationFeedFilter.account = account
|
NotificationFeedFilter.account = account
|
||||||
val notifFeedViewModel: NotificationViewModel = viewModel()
|
val notifFeedViewModel: NotificationViewModel = viewModel()
|
||||||
|
|
||||||
if (restartNotificationList) notifFeedViewModel.clear()
|
|
||||||
|
|
||||||
NavHost(navController, startDestination = Route.Home.route) {
|
NavHost(navController, startDestination = Route.Home.route) {
|
||||||
Route.Search.let { route ->
|
Route.Search.let { route ->
|
||||||
composable(route.route, route.arguments, content = {
|
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.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.BlockedUsers.route, content = { HiddenUsersScreen(accountViewModel, navController) })
|
||||||
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, navController) })
|
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, navController) })
|
||||||
|
|
||||||
|
@@ -37,8 +37,9 @@ sealed class Route(
|
|||||||
)
|
)
|
||||||
|
|
||||||
object Notification : Route(
|
object Notification : Route(
|
||||||
route = "Notification",
|
route = "Notification?scrollToTop={scrollToTop}",
|
||||||
icon = R.drawable.ic_notifications,
|
icon = R.drawable.ic_notifications,
|
||||||
|
arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }),
|
||||||
hasNewItems = { accountViewModel, cache -> notificationHasNewItems(accountViewModel, cache) }
|
hasNewItems = { accountViewModel, cache -> notificationHasNewItems(accountViewModel, cache) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -13,6 +13,7 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
|||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -33,7 +34,14 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@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()
|
val feedState by viewModel.feedContent.collectAsState()
|
||||||
|
|
||||||
var refreshing by remember { mutableStateOf(false) }
|
var refreshing by remember { mutableStateOf(false) }
|
||||||
@@ -57,10 +65,12 @@ fun CardFeedView(viewModel: CardFeedViewModel, accountViewModel: AccountViewMode
|
|||||||
is CardFeedState.Loaded -> {
|
is CardFeedState.Loaded -> {
|
||||||
refreshing = false
|
refreshing = false
|
||||||
FeedLoaded(
|
FeedLoaded(
|
||||||
state,
|
state = state,
|
||||||
accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
navController,
|
navController = navController,
|
||||||
routeForLastRead
|
routeForLastRead = routeForLastRead,
|
||||||
|
scrollStateKey = scrollStateKey,
|
||||||
|
scrollToTop = scrollToTop
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CardFeedState.Loading -> {
|
CardFeedState.Loading -> {
|
||||||
@@ -79,9 +89,21 @@ private fun FeedLoaded(
|
|||||||
state: CardFeedState.Loaded,
|
state: CardFeedState.Loaded,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
navController: NavController,
|
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(
|
LazyColumn(
|
||||||
contentPadding = PaddingValues(
|
contentPadding = PaddingValues(
|
||||||
|
@@ -3,6 +3,7 @@ package com.vitorpamplona.amethyst.ui.screen
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.LocalCacheState
|
import com.vitorpamplona.amethyst.model.LocalCacheState
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
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)
|
private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading)
|
||||||
val feedContent = _feedContent.asStateFlow()
|
val feedContent = _feedContent.asStateFlow()
|
||||||
|
|
||||||
|
private var lastAccount: Account? = null
|
||||||
private var lastNotes: List<Note>? = null
|
private var lastNotes: List<Note>? = null
|
||||||
|
|
||||||
private fun refresh() {
|
private fun refresh() {
|
||||||
@@ -45,18 +47,21 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
|||||||
private fun refreshSuspended() {
|
private fun refreshSuspended() {
|
||||||
val notes = dataSource.loadTop()
|
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
|
val oldNotesState = feedContent.value
|
||||||
if (lastNotesCopy != null && oldNotesState is CardFeedState.Loaded) {
|
if (lastNotesCopy != null && oldNotesState is CardFeedState.Loaded) {
|
||||||
val newCards = convertToCard(notes.minus(lastNotesCopy))
|
val newCards = convertToCard(notes.minus(lastNotesCopy))
|
||||||
if (newCards.isNotEmpty()) {
|
if (newCards.isNotEmpty()) {
|
||||||
lastNotes = notes
|
lastNotes = notes
|
||||||
|
lastAccount = (dataSource as? NotificationFeedFilter)?.account
|
||||||
updateFeed((oldNotesState.feed.value + newCards).distinctBy { it.id() }.sortedBy { it.createdAt() }.reversed())
|
updateFeed((oldNotesState.feed.value + newCards).distinctBy { it.id() }.sortedBy { it.createdAt() }.reversed())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val cards = convertToCard(notes)
|
val cards = convertToCard(notes)
|
||||||
lastNotes = notes
|
lastNotes = notes
|
||||||
|
lastAccount = (dataSource as? NotificationFeedFilter)?.account
|
||||||
updateFeed(cards)
|
updateFeed(cards)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ private data class ScrollState(val index: Int, val scrollOffset: Int)
|
|||||||
|
|
||||||
object ScrollStateKeys {
|
object ScrollStateKeys {
|
||||||
const val GLOBAL_SCREEN = "Global"
|
const val GLOBAL_SCREEN = "Global"
|
||||||
|
const val NOTIFICATION_SCREEN = "Notifications"
|
||||||
val HOME_FOLLOWS = Route.Home.base + "Follows"
|
val HOME_FOLLOWS = Route.Home.base + "Follows"
|
||||||
val HOME_REPLIES = Route.Home.base + "FollowsReplies"
|
val HOME_REPLIES = Route.Home.base + "FollowsReplies"
|
||||||
}
|
}
|
||||||
|
@@ -18,9 +18,15 @@ import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
|||||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||||
import com.vitorpamplona.amethyst.ui.screen.CardFeedView
|
import com.vitorpamplona.amethyst.ui.screen.CardFeedView
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys
|
||||||
|
|
||||||
@Composable
|
@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 accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
val account = accountState?.account ?: return
|
val account = accountState?.account ?: return
|
||||||
|
|
||||||
@@ -48,7 +54,14 @@ fun NotificationScreen(notifFeedViewModel: NotificationViewModel, accountViewMod
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(vertical = 0.dp)
|
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
app/src/main/res/drawable/alby.xml
Normal file
39
app/src/main/res/drawable/alby.xml
Normal 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>
|
Reference in New Issue
Block a user