Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: Pooling all adaptive tablet logic #78

Merged
merged 3 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaf
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.window.core.layout.WindowHeightSizeClass
import com.infomaniak.swisstransfer.ui.theme.LocalWindowAdaptiveInfo
import com.infomaniak.swisstransfer.ui.utils.isWindowLarge

/**
* A composable function that sets up a List-Detail interface using a three-pane scaffold navigator.
Expand All @@ -47,16 +48,13 @@ import androidx.window.core.layout.WindowHeightSizeClass
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun <T> TwoPaneScaffold(
windowAdaptiveInfo: WindowAdaptiveInfo,
listPane: @Composable ThreePaneScaffoldNavigator<T>.() -> Unit,
detailPane: @Composable ThreePaneScaffoldNavigator<T>.() -> Unit,
modifier: Modifier = Modifier,
) {
val windowAdaptiveInfo = LocalWindowAdaptiveInfo.current
val paneScaffoldDirective = calculatePaneScaffoldDirective(windowAdaptiveInfo)
val maxHorizontalPartitions = when (windowAdaptiveInfo.windowSizeClass.windowHeightSizeClass) {
WindowHeightSizeClass.COMPACT -> 1
else -> paneScaffoldDirective.maxHorizontalPartitions
}
val maxHorizontalPartitions = if (windowAdaptiveInfo.isWindowLarge()) 2 else 1
val navigator = rememberListDetailPaneScaffoldNavigator<T>(
scaffoldDirective = paneScaffoldDirective.copy(
maxHorizontalPartitions = maxHorizontalPartitions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
Expand All @@ -38,7 +37,6 @@ import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.TransferDetai
fun MainNavHost(
navController: NavHostController,
currentDestination: MainNavigation,
windowAdaptiveInfo: WindowAdaptiveInfo,
) {
NavHost(
navController = navController,
Expand All @@ -57,7 +55,7 @@ fun MainNavHost(
TransferDetailsScreen(transferId = transferDetails.transferId)
}
composable<SettingsDestination> {
SettingsScreenWrapper(windowAdaptiveInfo)
SettingsScreenWrapper()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package com.infomaniak.swisstransfer.ui.screen.main

import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
Expand All @@ -35,7 +34,6 @@ import com.infomaniak.swisstransfer.ui.utils.PreviewTablet
@Composable
fun MainScreen() {
val navController = rememberNavController()
val windowAdaptiveInfo = currentWindowAdaptiveInfo()

val navBackStackEntry by navController.currentBackStackEntryAsState()

Expand All @@ -46,9 +44,8 @@ fun MainScreen() {
MainScaffold(
navController = navController,
currentDestination = currentDestination,
windowAdaptiveInfo = windowAdaptiveInfo,
tabletTopAppBar = { BrandTobAppBar() },
content = { MainNavHost(navController, currentDestination, windowAdaptiveInfo) },
content = { MainNavHost(navController, currentDestination) },
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
Expand All @@ -30,25 +29,22 @@ import androidx.navigation.NavHostController
import com.infomaniak.swisstransfer.ui.components.BrandTobAppBar
import com.infomaniak.swisstransfer.ui.navigation.MainNavigation
import com.infomaniak.swisstransfer.ui.navigation.NavigationItem
import com.infomaniak.swisstransfer.ui.theme.LocalWindowAdaptiveInfo
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme
import com.infomaniak.swisstransfer.ui.utils.PreviewMobile
import com.infomaniak.swisstransfer.ui.utils.PreviewTablet

val LocalNavType = staticCompositionLocalOf { NavigationSuiteType.None }
import com.infomaniak.swisstransfer.ui.utils.isWindowLarge
import com.infomaniak.swisstransfer.ui.utils.isWindowSmall

@Composable
fun MainScaffold(
navController: NavHostController,
currentDestination: MainNavigation,
windowAdaptiveInfo: WindowAdaptiveInfo,
tabletTopAppBar: @Composable () -> Unit = {},
content: @Composable () -> Unit = {},
) {
val navType by rememberNavType(currentDestination, windowAdaptiveInfo)

CompositionLocalProvider(LocalNavType provides navType) {
MainScaffold(navType, currentDestination, navController::navigateToSelectedItem, tabletTopAppBar, content)
}
val navType by rememberNavType(currentDestination)
MainScaffold(navType, currentDestination, navController::navigateToSelectedItem, tabletTopAppBar, content)
}

@Composable
Expand All @@ -59,10 +55,12 @@ private fun MainScaffold(
tabletTopAppBar: @Composable () -> Unit,
content: @Composable () -> Unit,
) {
val windowAdaptiveInfo = LocalWindowAdaptiveInfo.current

Column {
if (navType == NavigationSuiteType.NavigationRail) tabletTopAppBar()
if (windowAdaptiveInfo.isWindowLarge()) tabletTopAppBar()
AppNavigationSuiteScaffold(navType, NavigationItem.entries, currentDestination, navigateToSelectedItem) {
if (navType == NavigationSuiteType.NavigationBar) {
if (windowAdaptiveInfo.isWindowSmall()) {
Column {
Box(modifier = Modifier.weight(1.0f)) { content() }
HorizontalDivider()
Expand All @@ -77,7 +75,7 @@ private fun MainScaffold(
@Composable
private fun rememberNavType(
currentDestination: MainNavigation,
windowAdaptiveInfo: WindowAdaptiveInfo,
windowAdaptiveInfo: WindowAdaptiveInfo = LocalWindowAdaptiveInfo.current,
): State<NavigationSuiteType> {

val showNavigation by remember(currentDestination) {
Expand All @@ -89,14 +87,18 @@ private fun rememberNavType(
return remember(showNavigation, windowAdaptiveInfo) {
derivedStateOf {
if (showNavigation) {
NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(windowAdaptiveInfo)
calculateFromAdaptiveInfo(windowAdaptiveInfo)
} else {
NavigationSuiteType.None
}
}
}
}

private fun calculateFromAdaptiveInfo(windowAdaptiveInfo: WindowAdaptiveInfo): NavigationSuiteType {
return if (windowAdaptiveInfo.isWindowLarge()) NavigationSuiteType.NavigationRail else NavigationSuiteType.NavigationBar
}

private fun NavHostController.navigateToSelectedItem(destination: MainNavigation) {
destination.enableTransition = false
navigate(destination) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,24 @@ package com.infomaniak.swisstransfer.ui.screen.main.components

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.Scaffold
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import com.infomaniak.swisstransfer.ui.theme.LocalWindowAdaptiveInfo
import com.infomaniak.swisstransfer.ui.utils.isWindowSmall

@Composable
fun PhoneTopAppBarScaffold(
phoneTopAppBar: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
content: @Composable (PaddingValues) -> Unit,
) {
val windowAdaptiveInfo = LocalWindowAdaptiveInfo.current

Scaffold(
topBar = { if (LocalNavType.current == NavigationSuiteType.NavigationBar) phoneTopAppBar() },
topBar = { if (windowAdaptiveInfo.isWindowSmall()) phoneTopAppBar() },
floatingActionButton = floatingActionButton,
) { contentPadding ->
val paddingValues = if (LocalNavType.current == NavigationSuiteType.NavigationBar) {
val paddingValues = if (windowAdaptiveInfo.isWindowSmall()) {
contentPadding
} else {
PaddingValues(all = 0.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.infomaniak.swisstransfer.ui.screen.main.sent

import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
Expand All @@ -28,29 +28,29 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.infomaniak.swisstransfer.ui.components.NewTransferFab
import com.infomaniak.swisstransfer.ui.components.NewTransferFabType
import com.infomaniak.swisstransfer.ui.screen.main.components.BrandTobAppBarScaffold
import com.infomaniak.swisstransfer.ui.screen.main.components.LocalNavType
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme
import com.infomaniak.swisstransfer.ui.utils.PreviewMobile
import com.infomaniak.swisstransfer.ui.utils.PreviewTablet
import com.infomaniak.swisstransfer.ui.utils.isWindowSmall

@Composable
fun SentScreen(
navigateToDetails: (transferId: Int) -> Unit,
sentViewModel: SentViewModel = hiltViewModel<SentViewModel>(),
) {
val transfers by sentViewModel.transfers.collectAsStateWithLifecycle()
val navType = LocalNavType.current

SentScreen(navType, transfers)
SentScreen(transfers)
}

@Composable
private fun SentScreen(navType: NavigationSuiteType, transfers: List<Any>?) {
private fun SentScreen(transfers: List<Any>?) {

if (transfers == null) return
val windowAdaptiveInfo = currentWindowAdaptiveInfo()

BrandTobAppBarScaffold(
floatingActionButton = {
if (navType == NavigationSuiteType.NavigationBar && transfers.isNotEmpty()) {
if (windowAdaptiveInfo.isWindowSmall() && transfers.isNotEmpty()) {
NewTransferFab(newTransferFabType = NewTransferFabType.BOTTOM_BAR)
}
},
Expand All @@ -68,27 +68,19 @@ private fun SentScreen(navType: NavigationSuiteType, transfers: List<Any>?) {
@PreviewMobile
@Composable
private fun SentScreenMobilePreview() {
val navType = LocalNavType.current
SwissTransferTheme {
Surface {
SentScreen(
navType = navType,
transfers = emptyList(),
)
SentScreen(transfers = emptyList())
}
}
}

@PreviewTablet
@Composable
private fun SentScreenTabletPreview() {
val navType = LocalNavType.current
SwissTransferTheme {
Surface {
SentScreen(
navType = navType,
transfers = emptyList(),
)
SentScreen(transfers = emptyList())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,11 @@ import com.infomaniak.swisstransfer.ui.utils.*
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun SettingsScreenWrapper(
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(),
settingsViewModel: SettingsViewModel = hiltViewModel<SettingsViewModel>(),
) {
val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null)
appSettings?.let { safeAppSettings ->
TwoPaneScaffold<SettingsOptionScreens>(
windowAdaptiveInfo = windowAdaptiveInfo,
listPane = { ListPane(this, safeAppSettings) },
detailPane = { DetailPane(safeAppSettings, settingsViewModel, navigator = this) },
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color

val LocalCustomTypography = staticCompositionLocalOf { Typography }
val LocalCustomColorScheme: ProvidableCompositionLocal<CustomColorScheme> = staticCompositionLocalOf { CustomColorScheme() }
val LocalWindowAdaptiveInfo = staticCompositionLocalOf<WindowAdaptiveInfo> { error("No WindowAdaptiveInfo provided") }

@Composable
fun SwissTransferTheme(
Expand All @@ -37,6 +40,7 @@ fun SwissTransferTheme(
LocalCustomTypography provides Typography,
LocalTextStyle provides Typography.bodyRegular,
LocalCustomColorScheme provides customColors,
LocalWindowAdaptiveInfo provides currentWindowAdaptiveInfo(),
) {
MaterialTheme(
colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Infomaniak SwissTransfer - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.swisstransfer.ui.utils

import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.window.core.layout.WindowHeightSizeClass
import androidx.window.core.layout.WindowWidthSizeClass

/**
* Determines if the current window is classified as a large window suitable for tablet devices.
*
* This is typically used to adapt the UI, such as displaying a list-detail layout or using a [NavigationRail]
* vs a [NavigationBar] for navigation.
*
* @return `true` if the window is large (tablet), `false` otherwise.
*/
fun WindowAdaptiveInfo.isWindowLarge(): Boolean = with(windowSizeClass) {
return windowWidthSizeClass == WindowWidthSizeClass.EXPANDED && windowHeightSizeClass != WindowHeightSizeClass.COMPACT
}

/**
* Determines if the current window is classified as a small window suitable for mobile devices.
*
* This is typically used to adapt the UI, such as displaying a list-detail layout or using a [NavigationRail]
* vs a [NavigationBar] for navigation.
*
* @return `true` if the window is small (mobile), `false` otherwise.
*/
fun WindowAdaptiveInfo.isWindowSmall(): Boolean = !isWindowLarge()
Loading