Fixing tab sliders from jumping from one state to the other

This commit is contained in:
Vitor Pamplona
2023-05-08 14:46:23 -04:00
parent 04c1300317
commit 4dcf38c492
5 changed files with 102 additions and 131 deletions
@@ -72,8 +72,8 @@ fun BookmarkListScreen(accountViewModel: AccountViewModel, navController: NavCon
} }
) )
} }
HorizontalPager(pageCount = 2, state = pagerState) { HorizontalPager(pageCount = 2, state = pagerState) { page ->
when (pagerState.currentPage) { when (page) {
0 -> FeedView(privateFeedViewModel, accountViewModel, navController, null) 0 -> FeedView(privateFeedViewModel, accountViewModel, navController, null)
1 -> FeedView(publicFeedViewModel, accountViewModel, navController, null) 1 -> FeedView(publicFeedViewModel, accountViewModel, navController, null)
} }
@@ -24,6 +24,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -44,6 +45,7 @@ import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
import com.vitorpamplona.amethyst.ui.dal.ChatroomListKnownFeedFilter import com.vitorpamplona.amethyst.ui.dal.ChatroomListKnownFeedFilter
import com.vitorpamplona.amethyst.ui.dal.ChatroomListNewFeedFilter import com.vitorpamplona.amethyst.ui.dal.ChatroomListNewFeedFilter
import com.vitorpamplona.amethyst.ui.screen.ChatroomListFeedView import com.vitorpamplona.amethyst.ui.screen.ChatroomListFeedView
import com.vitorpamplona.amethyst.ui.screen.FeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListKnownFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListKnownFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListNewFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListNewFeedViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -58,6 +60,48 @@ fun ChatroomListScreen(accountViewModel: AccountViewModel, navController: NavCon
val markKnownAsRead = remember { mutableStateOf(false) } val markKnownAsRead = remember { mutableStateOf(false) }
val markNewAsRead = remember { mutableStateOf(false) } val markNewAsRead = remember { mutableStateOf(false) }
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
ChatroomListKnownFeedFilter.account = account
val knownFeedViewModel: NostrChatroomListKnownFeedViewModel = viewModel()
ChatroomListNewFeedFilter.account = account
val newFeedViewModel: NostrChatroomListNewFeedViewModel = viewModel()
LaunchedEffect(accountViewModel) {
NostrChatroomListDataSource.account = account
NostrChatroomListDataSource.start()
knownFeedViewModel.invalidateData()
newFeedViewModel.invalidateData()
}
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrChatroomListDataSource.account = account
NostrChatroomListDataSource.start()
knownFeedViewModel.invalidateData()
newFeedViewModel.invalidateData()
}
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
}
}
val tabs by remember {
derivedStateOf {
listOf(
ChatroomListTabItem(R.string.known, knownFeedViewModel, markKnownAsRead),
ChatroomListTabItem(R.string.new_requests, newFeedViewModel, markNewAsRead)
)
}
}
Box(Modifier.fillMaxSize()) { Box(Modifier.fillMaxSize()) {
Column(Modifier.fillMaxHeight()) { Column(Modifier.fillMaxHeight()) {
Column( Column(
@@ -68,21 +112,17 @@ fun ChatroomListScreen(accountViewModel: AccountViewModel, navController: NavCon
backgroundColor = MaterialTheme.colors.background, backgroundColor = MaterialTheme.colors.background,
selectedTabIndex = pagerState.currentPage selectedTabIndex = pagerState.currentPage
) { ) {
Tab( tabs.forEachIndexed { index, tab ->
selected = pagerState.currentPage == 0, Tab(
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(0) } }, selected = pagerState.currentPage == index,
text = { text = {
Text(text = stringResource(R.string.known)) Text(text = stringResource(tab.resource))
} },
) onClick = {
coroutineScope.launch { pagerState.animateScrollToPage(index) }
Tab( }
selected = pagerState.currentPage == 1, )
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(1) } }, }
text = {
Text(text = stringResource(R.string.new_requests))
}
)
} }
IconButton( IconButton(
@@ -107,102 +147,20 @@ fun ChatroomListScreen(accountViewModel: AccountViewModel, navController: NavCon
} }
} }
HorizontalPager(pageCount = 2, state = pagerState) { HorizontalPager(pageCount = 2, state = pagerState) { page ->
when (pagerState.currentPage) { ChatroomListFeedView(
0 -> TabKnown(accountViewModel, navController, markKnownAsRead) viewModel = tabs[page].viewModel,
1 -> TabNew(accountViewModel, navController, markNewAsRead) accountViewModel = accountViewModel,
} navController = navController,
markAsRead = tabs[page].markAsRead
)
} }
} }
} }
} }
} }
@Composable class ChatroomListTabItem(val resource: Int, val viewModel: FeedViewModel, val markAsRead: MutableState<Boolean>)
fun TabKnown(
accountViewModel: AccountViewModel,
navController: NavController,
markAsRead: MutableState<Boolean>
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
ChatroomListKnownFeedFilter.account = account
val feedViewModel: NostrChatroomListKnownFeedViewModel = viewModel()
LaunchedEffect(accountViewModel) {
NostrChatroomListDataSource.account = account
NostrChatroomListDataSource.start()
feedViewModel.invalidateData()
}
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrChatroomListDataSource.account = account
NostrChatroomListDataSource.start()
feedViewModel.invalidateData()
}
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
}
}
Column(Modifier.fillMaxHeight()) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
ChatroomListFeedView(feedViewModel, accountViewModel, navController, markAsRead)
}
}
}
@Composable
fun TabNew(
accountViewModel: AccountViewModel,
navController: NavController,
markAsRead: MutableState<Boolean>
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
ChatroomListNewFeedFilter.account = account
val feedViewModel: NostrChatroomListNewFeedViewModel = viewModel()
LaunchedEffect(accountViewModel) {
NostrChatroomListDataSource.account = account
NostrChatroomListDataSource.start()
feedViewModel.invalidateData()
}
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrChatroomListDataSource.account = account
NostrChatroomListDataSource.start()
feedViewModel.invalidateData()
}
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
}
}
Column(Modifier.fillMaxHeight()) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
ChatroomListFeedView(feedViewModel, accountViewModel, navController, markAsRead)
}
}
}
@Composable @Composable
fun ChatroomTabMenu( fun ChatroomTabMenu(
@@ -53,8 +53,8 @@ fun HiddenUsersScreen(accountViewModel: AccountViewModel, navController: NavCont
} }
) )
} }
HorizontalPager(pageCount = 1, state = pagerState) { HorizontalPager(pageCount = 1, state = pagerState) { page ->
when (pagerState.currentPage) { when (page) {
0 -> UserFeedView(feedViewModel, accountViewModel, navController) 0 -> UserFeedView(feedViewModel, accountViewModel, navController)
} }
} }
@@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter
import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountDialog import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountDialog
import com.vitorpamplona.amethyst.ui.screen.FeedView import com.vitorpamplona.amethyst.ui.screen.FeedView
import com.vitorpamplona.amethyst.ui.screen.FeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys
@@ -85,6 +86,15 @@ fun HomeScreen(
} }
} }
val tabs by remember(homeFeedViewModel, repliesFeedViewModel) {
mutableStateOf(
listOf(
TabItem(R.string.new_threads, homeFeedViewModel, Route.Home.base + "Follows", ScrollStateKeys.HOME_FOLLOWS),
TabItem(R.string.conversations, repliesFeedViewModel, Route.Home.base + "FollowsReplies", ScrollStateKeys.HOME_REPLIES)
)
)
}
Column(Modifier.fillMaxHeight()) { Column(Modifier.fillMaxHeight()) {
Column( Column(
modifier = Modifier.padding(vertical = 0.dp) modifier = Modifier.padding(vertical = 0.dp)
@@ -93,28 +103,31 @@ fun HomeScreen(
backgroundColor = MaterialTheme.colors.background, backgroundColor = MaterialTheme.colors.background,
selectedTabIndex = pagerState.currentPage selectedTabIndex = pagerState.currentPage
) { ) {
Tab( tabs.forEachIndexed { index, tab ->
selected = pagerState.currentPage == 0, Tab(
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(0) } }, selected = pagerState.currentPage == index,
text = { text = {
Text(text = stringResource(R.string.new_threads)) Text(text = stringResource(tab.resource))
} },
) onClick = {
coroutineScope.launch { pagerState.animateScrollToPage(index) }
Tab( }
selected = pagerState.currentPage == 1, )
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(1) } },
text = {
Text(text = stringResource(R.string.conversations))
}
)
}
HorizontalPager(pageCount = 2, state = pagerState) {
when (pagerState.currentPage) {
0 -> FeedView(homeFeedViewModel, accountViewModel, navController, Route.Home.base + "Follows", ScrollStateKeys.HOME_FOLLOWS, scrollToTop)
1 -> FeedView(repliesFeedViewModel, accountViewModel, navController, Route.Home.base + "FollowsReplies", ScrollStateKeys.HOME_REPLIES, scrollToTop)
} }
} }
HorizontalPager(pageCount = 2, state = pagerState) { page ->
FeedView(
viewModel = tabs[page].viewModel,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = tabs[page].routeForLastRead,
scrollStateKey = tabs[page].scrollStateKey,
scrollToTop = scrollToTop
)
}
} }
} }
} }
class TabItem(val resource: Int, val viewModel: FeedViewModel, val routeForLastRead: String, val scrollStateKey: String)
@@ -288,8 +288,8 @@ fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController:
modifier = with(LocalDensity.current) { modifier = with(LocalDensity.current) {
Modifier.height((columnSize.height - tabsSize.height).toDp()) Modifier.height((columnSize.height - tabsSize.height).toDp())
} }
) { ) { page ->
when (pagerState.currentPage) { when (page) {
0 -> TabNotesNewThreads(accountViewModel, navController) 0 -> TabNotesNewThreads(accountViewModel, navController)
1 -> TabNotesConversations(accountViewModel, navController) 1 -> TabNotesConversations(accountViewModel, navController)
2 -> TabFollows(baseUser, accountViewModel, navController) 2 -> TabFollows(baseUser, accountViewModel, navController)