diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt index f59437670..52c54034c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt @@ -83,7 +83,7 @@ fun keyboardAsState(): State { @Composable fun AppBottomBar(navController: NavHostController, accountViewModel: AccountViewModel) { - val currentRoute = currentRoute(navController) + val currentRoute = currentRoute(navController)?.substringBefore("?") val coroutineScope = rememberCoroutineScope() val isKeyboardOpen by keyboardAsState() @@ -101,10 +101,10 @@ fun AppBottomBar(navController: NavHostController, accountViewModel: AccountView bottomNavigationItems.forEach { item -> BottomNavigationItem( icon = { NotifiableIcon(item, currentRoute, accountViewModel) }, - selected = currentRoute == item.route, + selected = currentRoute == item.base, onClick = { coroutineScope.launch { - if (currentRoute != item.route) { + if (currentRoute != item.base) { navController.navigate(item.route) { navController.graph.startDestinationRoute?.let { start -> popUpTo(start) @@ -114,8 +114,7 @@ fun AppBottomBar(navController: NavHostController, accountViewModel: AccountView restoreState = true } } else { - // TODO: Make it scrool to the top - navController.navigate(item.route) { + navController.navigate("${item.base}?forceRefresh=${true}") { navController.graph.startDestinationRoute?.let { start -> popUpTo(start) { inclusive = item.route == Route.Home.route } restoreState = true @@ -136,12 +135,12 @@ fun AppBottomBar(navController: NavHostController, accountViewModel: AccountView @Composable private fun NotifiableIcon(item: Route, currentRoute: String?, accountViewModel: AccountViewModel) { - Box(Modifier.size(if ("Home" == item.route) 25.dp else 23.dp)) { + Box(Modifier.size(if ("Home" == item.base) 25.dp else 23.dp)) { Icon( painter = painterResource(id = item.icon), null, - modifier = Modifier.size(if ("Home" == item.route) 24.dp else 20.dp), - tint = if (currentRoute == item.route) MaterialTheme.colors.primary else Color.Unspecified + modifier = Modifier.size(if ("Home" == item.base) 24.dp else 20.dp), + tint = if (currentRoute == item.base) MaterialTheme.colors.primary else Color.Unspecified ) val accountState by accountViewModel.accountLiveData.observeAsState() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt index 66bed57fa..69b7ecba8 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt @@ -35,17 +35,32 @@ sealed class Route( val arguments: List = emptyList(), val buildScreen: (AccountViewModel, AccountStateViewModel, NavController) -> @Composable (NavBackStackEntry) -> Unit ) { + val base: String + get() = route.substringBefore("?") + object Home : Route( - "Home", + "Home?forceRefresh={forceRefresh}", R.drawable.ic_home, + arguments = listOf(navArgument("forceRefresh") { type = NavType.BoolType; defaultValue = false }), hasNewItems = { acc, cache, ctx -> homeHasNewItems(acc, cache, ctx) }, - buildScreen = { acc, accSt, nav -> { _ -> HomeScreen(acc, nav) } } + buildScreen = { acc, accSt, nav -> + { backStackEntry -> + HomeScreen(acc, nav, backStackEntry.arguments?.getBoolean("forceRefresh", false)) + } + } ) + object Search : Route( - "Search", + "Search?forceRefresh={forceRefresh}", R.drawable.ic_globe, - buildScreen = { acc, accSt, nav -> { _ -> SearchScreen(acc, nav) } } + arguments = listOf(navArgument("forceRefresh") { type = NavType.BoolType; defaultValue = false }), + buildScreen = { acc, accSt, nav -> + { backStackEntry -> + SearchScreen(acc, nav, backStackEntry.arguments?.getBoolean("forceRefresh", false)) + } + } ) + object Notification : Route( "Notification", R.drawable.ic_notifications, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt index aed87aaee..bca089f1e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt @@ -19,6 +19,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 @@ -40,11 +41,12 @@ fun FeedView( accountViewModel: AccountViewModel, navController: NavController, routeForLastRead: String?, - scrollStateKey: String? = null + scrollStateKey: String? = null, + forceRefresh: Boolean? = false ) { val feedState by viewModel.feedContent.collectAsState() - var refreshing by remember { mutableStateOf(false) } + var refreshing by remember { mutableStateOf(forceRefresh!!) } val refresh = { refreshing = true; viewModel.refresh(); refreshing = false } val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh = refresh) @@ -74,7 +76,8 @@ fun FeedView( routeForLastRead, accountViewModel, navController, - scrollStateKey + scrollStateKey, + forceRefresh!! ) } @@ -95,7 +98,8 @@ private fun FeedLoaded( routeForLastRead: String?, accountViewModel: AccountViewModel, navController: NavController, - scrollStateKey: String? + scrollStateKey: String?, + forceRefresh: Boolean = false ) { val listState = if (scrollStateKey != null) { rememberForeverLazyListState(scrollStateKey) @@ -103,6 +107,12 @@ private fun FeedLoaded( rememberLazyListState() } + if (forceRefresh) { + LaunchedEffect(Unit) { + listState.animateScrollToItem(0) + } + } + LazyColumn( contentPadding = PaddingValues( top = 10.dp, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LazyListState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LazyListState.kt index e6eb0c39e..0a4a2d5cf 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LazyListState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LazyListState.kt @@ -4,10 +4,17 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.saveable.rememberSaveable +import com.vitorpamplona.amethyst.ui.navigation.Route private val savedScrollStates = mutableMapOf() private data class ScrollState(val index: Int, val scrollOffset: Int) +object ScrollStateKeys { + const val GLOBAL_SCREEN = "Global" + val HOME_FOLLOWS = Route.Home.base + "Follows" + val HOME_REPLIES = Route.Home.base + "FollowsReplies" +} + @Composable fun rememberForeverLazyListState( key: String, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt index b5a8e35aa..0e80382ec 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt @@ -34,11 +34,12 @@ import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.screen.FeedView import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel +import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys import kotlinx.coroutines.launch @OptIn(ExperimentalPagerApi::class) @Composable -fun HomeScreen(accountViewModel: AccountViewModel, navController: NavController) { +fun HomeScreen(accountViewModel: AccountViewModel, navController: NavController, forceRefresh: Boolean? = false) { val accountState by accountViewModel.accountLiveData.observeAsState() val account = accountState?.account ?: return @@ -106,8 +107,8 @@ fun HomeScreen(accountViewModel: AccountViewModel, navController: NavController) } HorizontalPager(count = 2, state = pagerState) { when (pagerState.currentPage) { - 0 -> FeedView(feedViewModel, accountViewModel, navController, Route.Home.route + "Follows", Route.Home.route + "Follows") - 1 -> FeedView(feedViewModelReplies, accountViewModel, navController, Route.Home.route + "FollowsReplies", Route.Home.route + "FollowsReplies") + 0 -> FeedView(feedViewModel, accountViewModel, navController, Route.Home.base + "Follows", ScrollStateKeys.HOME_FOLLOWS, forceRefresh) + 1 -> FeedView(feedViewModelReplies, accountViewModel, navController, Route.Home.base + "FollowsReplies", ScrollStateKeys.HOME_REPLIES) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt index fa698b275..62345d678 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt @@ -61,7 +61,7 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun fun FloatingButton(navController: NavHostController, accountViewModel: AccountStateViewModel) { val accountState by accountViewModel.accountContent.collectAsState() - if (currentRoute(navController) == Route.Home.route) { + if (currentRoute(navController)?.substringBefore("?") == Route.Home.base) { Crossfade(targetState = accountState, animationSpec = tween(durationMillis = 100)) { state -> when (state) { is AccountState.LoggedInViewOnly -> { @@ -77,7 +77,7 @@ fun FloatingButton(navController: NavHostController, accountViewModel: AccountSt } } - if (currentRoute(navController) == Route.Message.route) { + if (currentRoute(navController) == Route.Message.base) { Crossfade(targetState = accountState, animationSpec = tween(durationMillis = 100)) { state -> when (state) { is AccountState.LoggedInViewOnly -> { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt index b9000e89b..f74e4f553 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt @@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.screen.FeedView import com.vitorpamplona.amethyst.ui.screen.NostrGlobalFeedViewModel +import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.collectLatest @@ -78,7 +79,7 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.channels.Channel as CoroutineChannel @Composable -fun SearchScreen(accountViewModel: AccountViewModel, navController: NavController) { +fun SearchScreen(accountViewModel: AccountViewModel, navController: NavController, forceRefresh: Boolean? = false) { val accountState by accountViewModel.accountLiveData.observeAsState() val account = accountState?.account ?: return @@ -114,7 +115,7 @@ fun SearchScreen(accountViewModel: AccountViewModel, navController: NavControlle modifier = Modifier.padding(vertical = 0.dp) ) { SearchBar(accountViewModel, navController) - FeedView(feedViewModel, accountViewModel, navController, null, "Global") + FeedView(feedViewModel, accountViewModel, navController, null, ScrollStateKeys.GLOBAL_SCREEN, forceRefresh) } } }