Merge pull request #1860 from davotoula/swipe-on-main-screen
Swipe to switch tabs. Main screen and messages
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
|
||||
private const val PAGER_ZONE_FRACTION = 0.5f
|
||||
|
||||
fun Modifier.zonedDrawerSwipe(
|
||||
pagerState: PagerState,
|
||||
openDrawer: () -> Unit,
|
||||
): Modifier =
|
||||
composed {
|
||||
var widthPx by remember { mutableFloatStateOf(1f) }
|
||||
var gestureStartX by remember { mutableFloatStateOf(0f) }
|
||||
var gestureStartPage by remember { mutableIntStateOf(0) }
|
||||
var drawerOpened by remember { mutableStateOf(false) }
|
||||
|
||||
val connection =
|
||||
remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPreScroll(
|
||||
available: Offset,
|
||||
source: NestedScrollSource,
|
||||
): Offset {
|
||||
if (source != NestedScrollSource.UserInput) return Offset.Zero
|
||||
if (drawerOpened) return Offset(available.x, 0f)
|
||||
|
||||
// Non-first pages in the drawer zone: intercept before the
|
||||
// pager consumes the delta to page backwards.
|
||||
if (available.x > 0f) {
|
||||
val wasOnFirstPage = gestureStartPage == 0
|
||||
val isInPagerZone = gestureStartX < widthPx * PAGER_ZONE_FRACTION
|
||||
|
||||
if (!wasOnFirstPage && !isInPagerZone) {
|
||||
drawerOpened = true
|
||||
openDrawer()
|
||||
return Offset(available.x, 0f)
|
||||
}
|
||||
}
|
||||
return Offset.Zero
|
||||
}
|
||||
|
||||
override fun onPostScroll(
|
||||
consumed: Offset,
|
||||
available: Offset,
|
||||
source: NestedScrollSource,
|
||||
): Offset {
|
||||
if (source != NestedScrollSource.UserInput) return Offset.Zero
|
||||
if (drawerOpened) return Offset(available.x, 0f)
|
||||
|
||||
// First page: open drawer only with unconsumed right-swipe
|
||||
// so child LazyRows can scroll first.
|
||||
if (available.x > 0f && gestureStartPage == 0) {
|
||||
drawerOpened = true
|
||||
openDrawer()
|
||||
return Offset(available.x, 0f)
|
||||
}
|
||||
return Offset.Zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this
|
||||
.onSizeChanged { widthPx = it.width.toFloat() }
|
||||
.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
val down = awaitFirstDown(requireUnconsumed = false)
|
||||
gestureStartX = down.position.x
|
||||
gestureStartPage = pagerState.currentPage
|
||||
drawerOpened = false
|
||||
}
|
||||
}.nestedScroll(connection)
|
||||
}
|
||||
@@ -37,8 +37,10 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.util.Consumer
|
||||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.crashreports.DisplayCrashMessages
|
||||
@@ -142,7 +144,17 @@ fun AppNavigation(
|
||||
) {
|
||||
val nav = rememberNav()
|
||||
|
||||
AccountSwitcherAndLeftDrawerLayout(accountViewModel, accountSessionManager, nav) {
|
||||
val navBackStackEntry by nav.controller.currentBackStackEntryAsState()
|
||||
val isTabPagerRoute =
|
||||
navBackStackEntry?.destination?.let { dest ->
|
||||
dest.hasRoute<Route.Home>() || dest.hasRoute<Route.Message>()
|
||||
} ?: false
|
||||
val drawerGesturesEnabled =
|
||||
!isTabPagerRoute ||
|
||||
nav.drawerState.isOpen ||
|
||||
nav.drawerState.targetValue != nav.drawerState.currentValue
|
||||
|
||||
AccountSwitcherAndLeftDrawerLayout(accountViewModel, accountSessionManager, nav, drawerGesturesEnabled) {
|
||||
NavHost(
|
||||
navController = nav.controller,
|
||||
startDestination = Route.Home,
|
||||
|
||||
+2
@@ -49,6 +49,7 @@ fun AccountSwitcherAndLeftDrawerLayout(
|
||||
accountViewModel: AccountViewModel,
|
||||
accountSessionManager: AccountSessionManager,
|
||||
nav: INav,
|
||||
gesturesEnabled: Boolean = true,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -83,6 +84,7 @@ fun AccountSwitcherAndLeftDrawerLayout(
|
||||
|
||||
ModalNavigationDrawer(
|
||||
drawerState = nav.drawerState,
|
||||
gesturesEnabled = gesturesEnabled,
|
||||
drawerContent = {
|
||||
DrawerContent(nav, openSheetFunction, accountViewModel)
|
||||
BackHandler(enabled = nav.drawerState.isOpen, nav::closeDrawer)
|
||||
|
||||
+7
-1
@@ -48,6 +48,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.components.zonedDrawerSwipe
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@@ -124,7 +125,12 @@ fun MessagesPager(
|
||||
HorizontalPager(
|
||||
contentPadding = paddingValues,
|
||||
state = pagerState,
|
||||
userScrollEnabled = false,
|
||||
userScrollEnabled = true,
|
||||
modifier =
|
||||
Modifier.zonedDrawerSwipe(
|
||||
pagerState = pagerState,
|
||||
openDrawer = nav::openDrawer,
|
||||
),
|
||||
) { page ->
|
||||
ChatroomListFeedView(
|
||||
feedContentState = tabs[page].feedContentState,
|
||||
|
||||
+7
-1
@@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.model.TopFilter
|
||||
import com.vitorpamplona.amethyst.service.OnlineChecker
|
||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.zonedDrawerSwipe
|
||||
import com.vitorpamplona.amethyst.ui.feeds.ChannelFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.ChannelFeedState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.PagerStateKeys
|
||||
@@ -211,7 +212,12 @@ private fun HomePages(
|
||||
HorizontalPager(
|
||||
contentPadding = it,
|
||||
state = pagerState,
|
||||
userScrollEnabled = false,
|
||||
userScrollEnabled = true,
|
||||
modifier =
|
||||
Modifier.zonedDrawerSwipe(
|
||||
pagerState = pagerState,
|
||||
openDrawer = nav::openDrawer,
|
||||
),
|
||||
) { page ->
|
||||
HomeFeeds(
|
||||
feedState = tabs[page].feedState,
|
||||
|
||||
Reference in New Issue
Block a user