feat: configurable bottom navigation • part 1 (#1111)

This commit is contained in:
Diego Beraldin 2024-07-11 08:31:57 +02:00 committed by GitHub
parent 3a1b04d010
commit e8199ed40f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 942 additions and 816 deletions

View File

@ -19,6 +19,10 @@ sealed interface TabNavigationSection {
data object Profile : TabNavigationSection data object Profile : TabNavigationSection
data object Inbox : TabNavigationSection data object Inbox : TabNavigationSection
data object Settings : TabNavigationSection
data object Bookmarks : TabNavigationSection
} }
sealed interface ComposeEvent { sealed interface ComposeEvent {

View File

@ -0,0 +1,31 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.settings
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.tab.Tab
import cafe.adriel.voyager.navigator.tab.TabOptions
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.main.SettingsScreen
object SettingsTab : Tab {
override val options: TabOptions
@Composable
get() {
val icon = rememberVectorPainter(Icons.Default.Settings)
val title = LocalStrings.current.navigationSettings
return TabOptions(
index = 4u,
title = title,
icon = icon,
)
}
@Composable
override fun Content() {
Navigator(SettingsScreen())
}
}

View File

@ -33,8 +33,9 @@ kotlin {
dependencies { dependencies {
implementation(compose.runtime) implementation(compose.runtime)
implementation(compose.foundation) implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.material) implementation(compose.material)
implementation(compose.material3)
implementation(compose.materialIconsExtended)
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
implementation(compose.components.resources) implementation(compose.components.resources)

View File

@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy
import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
interface MainMviModel : interface MainMviModel :
MviModel<MainMviModel.Intent, MainMviModel.UiState, MainMviModel.Effect>, MviModel<MainMviModel.Intent, MainMviModel.UiState, MainMviModel.Effect>,
@ -18,6 +19,13 @@ interface MainMviModel :
val bottomBarOffsetHeightPx: Float = 0f, val bottomBarOffsetHeightPx: Float = 0f,
val customProfileUrl: String? = null, val customProfileUrl: String? = null,
val isLogged: Boolean = false, val isLogged: Boolean = false,
val bottomBarSections: List<TabNavigationSection> =
listOf(
TabNavigationSection.Home,
TabNavigationSection.Explore,
TabNavigationSection.Inbox,
TabNavigationSection.Profile,
),
) )
sealed interface Effect { sealed interface Effect {

View File

@ -33,6 +33,7 @@ import androidx.compose.ui.unit.toSize
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.koin.getScreenModel import cafe.adriel.voyager.koin.getScreenModel
import cafe.adriel.voyager.navigator.tab.CurrentTab import cafe.adriel.voyager.navigator.tab.CurrentTab
import cafe.adriel.voyager.navigator.tab.Tab
import cafe.adriel.voyager.navigator.tab.TabNavigator import cafe.adriel.voyager.navigator.tab.TabNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
@ -42,13 +43,10 @@ import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getDrawerCoor
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.ui.InboxTab
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.ui.ProfileTab
import com.github.diegoberaldin.raccoonforlemmy.feature.search.ui.ExploreTab
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.main.SettingsScreen import com.github.diegoberaldin.raccoonforlemmy.feature.settings.main.SettingsScreen
import com.github.diegoberaldin.raccoonforlemmy.ui.navigation.TabNavigationItem import com.github.diegoberaldin.raccoonforlemmy.navigation.TabNavigationItem
import com.github.diegoberaldin.raccoonforlemmy.unit.manageaccounts.ManageAccountsScreen import com.github.diegoberaldin.raccoonforlemmy.unit.manageaccounts.ManageAccountsScreen
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
@ -225,6 +223,37 @@ internal object MainScreen : Screen {
uiFontSizeWorkaround = true uiFontSizeWorkaround = true
}.launchIn(this) }.launchIn(this)
} }
fun handleOnLongPress(
tab: Tab,
section: TabNavigationSection,
) {
when (section) {
TabNavigationSection.Explore -> {
tabNavigator.current = tab
navigationCoordinator.setCurrentSection(TabNavigationSection.Explore)
scope.launch {
notificationCenter.send(NotificationCenterEvent.OpenSearchInExplore)
}
}
TabNavigationSection.Inbox -> {
if (uiState.isLogged) {
model.reduce(MainMviModel.Intent.ReadAllInbox)
}
}
TabNavigationSection.Profile -> {
if (uiState.isLogged) {
val screen = ManageAccountsScreen()
navigationCoordinator.showBottomSheet(screen)
}
}
else -> Unit
}
}
if (uiFontSizeWorkaround) { if (uiFontSizeWorkaround) {
NavigationBar( NavigationBar(
modifier = modifier =
@ -248,45 +277,27 @@ internal object MainScreen : Screen {
), ),
tonalElevation = 0.dp, tonalElevation = 0.dp,
) { ) {
for (section in uiState.bottomBarSections) {
TabNavigationItem( TabNavigationItem(
tab = HomeTab, section = section,
withText = titleVisible,
)
TabNavigationItem(
tab = ExploreTab,
withText = titleVisible, withText = titleVisible,
customIconUrl =
if (section == TabNavigationSection.Profile) {
uiState.customProfileUrl
} else {
null
},
onClick =
rememberCallbackArgs { tab ->
tabNavigator.current = tab
navigationCoordinator.setCurrentSection(section)
},
onLongPress = onLongPress =
rememberCallback { rememberCallbackArgs { tab ->
tabNavigator.current = ExploreTab handleOnLongPress(tab, section)
navigationCoordinator.setCurrentSection(TabNavigationSection.Explore)
scope.launch {
notificationCenter.send(NotificationCenterEvent.OpenSearchInExplore)
}
}, },
) )
TabNavigationItem(
tab = InboxTab,
withText = titleVisible,
onLongPress =
rememberCallback(model) {
if (uiState.isLogged) {
model.reduce(MainMviModel.Intent.ReadAllInbox)
} }
},
)
TabNavigationItem(
tab = ProfileTab,
withText = titleVisible,
customIconUrl = uiState.customProfileUrl,
onLongPress =
rememberCallback {
if (uiState.isLogged) {
val screen = ManageAccountsScreen()
navigationCoordinator.showBottomSheet(screen)
}
},
)
} }
} }
} }

View File

@ -0,0 +1,37 @@
package com.github.diegoberaldin.raccoonforlemmy.navigation
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.tab.Tab
import cafe.adriel.voyager.navigator.tab.TabOptions
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsType
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.toInt
object BookmarksTab : Tab {
override val options: TabOptions
@Composable
get() {
val icon = rememberVectorPainter(Icons.Default.Bookmark)
val title = LocalStrings.current.navigationDrawerTitleBookmarks
return TabOptions(
index = 5u,
title = title,
icon = icon,
)
}
@Composable
override fun Content() {
Navigator(
FilteredContentsScreen(
type = FilteredContentsType.Bookmarks.toInt(),
),
)
}
}

View File

@ -1,4 +1,4 @@
package com.github.diegoberaldin.raccoonforlemmy.ui.navigation package com.github.diegoberaldin.raccoonforlemmy.navigation
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
@ -24,7 +24,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.Tab
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
@ -32,39 +31,28 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomI
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.ui.InboxTab import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.ui.InboxTab
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.ui.ProfileTab import com.github.diegoberaldin.raccoonforlemmy.feature.profile.ui.ProfileTab
import com.github.diegoberaldin.raccoonforlemmy.feature.search.ui.ExploreTab import com.github.diegoberaldin.raccoonforlemmy.feature.search.ui.ExploreTab
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.SettingsTab
@Composable @Composable
internal fun RowScope.TabNavigationItem( internal fun RowScope.TabNavigationItem(
tab: Tab, section: TabNavigationSection,
withText: Boolean = true, withText: Boolean = true,
customIconUrl: String? = null, customIconUrl: String? = null,
onLongPress: (() -> Unit)? = null, onClick: ((Tab) -> Unit)? = null,
onLongPress: ((Tab) -> Unit)? = null,
) { ) {
val tabNavigator = LocalTabNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() } val navigationCoordinator = remember { getNavigationCoordinator() }
val unread by navigationCoordinator.inboxUnread.collectAsState() val unread by navigationCoordinator.inboxUnread.collectAsState()
val color = val currentSection by navigationCoordinator.currentSection.collectAsState()
if (tabNavigator.current == tab) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.outline
}
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
val tab = section.toTab()
fun handleClick() { fun handleClick() {
tabNavigator.current = tab onClick?.invoke(tab)
val section =
when (tab) {
ExploreTab -> TabNavigationSection.Explore
ProfileTab -> TabNavigationSection.Profile
InboxTab -> TabNavigationSection.Inbox
else -> TabNavigationSection.Home
}
navigationCoordinator.setCurrentSection(section)
} }
val pointerInputModifier = val pointerInputModifier =
@ -80,14 +68,14 @@ internal fun RowScope.TabNavigationItem(
handleClick() handleClick()
}, },
onLongPress = { onLongPress = {
onLongPress?.invoke() onLongPress?.invoke(tab)
}, },
) )
} }
NavigationBarItem( NavigationBarItem(
onClick = ::handleClick, onClick = ::handleClick,
selected = tabNavigator.current == tab, selected = section == currentSection,
interactionSource = interactionSource, interactionSource = interactionSource,
icon = { icon = {
val content = @Composable { val content = @Composable {
@ -107,7 +95,6 @@ internal fun RowScope.TabNavigationItem(
modifier = pointerInputModifier, modifier = pointerInputModifier,
painter = tab.options.icon ?: rememberVectorPainter(Icons.Default.Home), painter = tab.options.icon ?: rememberVectorPainter(Icons.Default.Home),
contentDescription = null, contentDescription = null,
tint = color,
) )
} }
} }
@ -141,7 +128,6 @@ internal fun RowScope.TabNavigationItem(
Text( Text(
modifier = Modifier, modifier = Modifier,
text = tab.options.title, text = tab.options.title,
color = color,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
@ -149,3 +135,13 @@ internal fun RowScope.TabNavigationItem(
}, },
) )
} }
private fun TabNavigationSection.toTab(): Tab =
when (this) {
TabNavigationSection.Explore -> ExploreTab
TabNavigationSection.Profile -> ProfileTab
TabNavigationSection.Inbox -> InboxTab
TabNavigationSection.Settings -> SettingsTab
TabNavigationSection.Bookmarks -> BookmarksTab
else -> HomeTab
}

View File

@ -5,10 +5,10 @@ import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@ -25,6 +25,7 @@ import androidx.compose.material.icons.filled.ArrowCircleDown
import androidx.compose.material.icons.filled.ArrowCircleUp import androidx.compose.material.icons.filled.ArrowCircleUp
import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.pullrefresh.PullRefreshIndicator 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
@ -49,6 +50,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -60,6 +62,7 @@ import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.koin.getScreenModel import cafe.adriel.voyager.koin.getScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Dimensions
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.FloatingActionButtonMenu import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.FloatingActionButtonMenu
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.FloatingActionButtonMenuItem import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.FloatingActionButtonMenuItem
@ -74,12 +77,14 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.PostCardPl
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.di.getFabNestedScrollConnection import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.di.getFabNestedScrollConnection
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.LikedTypeSheet import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.LikedTypeSheet
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getDrawerCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.ActionOnSwipe import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.ActionOnSwipe
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalPixel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.readableHandle import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.readableHandle
@ -97,6 +102,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import kotlin.math.roundToInt
class FilteredContentsScreen( class FilteredContentsScreen(
private val type: Int, private val type: Int,
@ -111,6 +117,7 @@ class FilteredContentsScreen(
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() } val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState() val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val navigationCoordinator = remember { getNavigationCoordinator() } val navigationCoordinator = remember { getNavigationCoordinator() }
val drawerCoordinator = remember { getDrawerCoordinator() }
val settingsRepository = remember { getSettingsRepository() } val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState() val settings by settingsRepository.currentSettings.collectAsState()
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
@ -137,7 +144,8 @@ class FilteredContentsScreen(
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(model) { LaunchedEffect(model) {
model.effects.onEach { effect -> model.effects
.onEach { effect ->
when (effect) { when (effect) {
FilteredContentsMviModel.Effect.BackToTop -> { FilteredContentsMviModel.Effect.BackToTop -> {
runCatching { runCatching {
@ -150,7 +158,8 @@ class FilteredContentsScreen(
}.launchIn(this) }.launchIn(this)
} }
LaunchedEffect(navigationCoordinator) { LaunchedEffect(navigationCoordinator) {
navigationCoordinator.globalMessage.onEach { message -> navigationCoordinator.globalMessage
.onEach { message ->
snackbarHostState.showSnackbar( snackbarHostState.showSnackbar(
message = message, message = message,
) )
@ -160,12 +169,26 @@ class FilteredContentsScreen(
Scaffold( Scaffold(
modifier = Modifier.background(MaterialTheme.colorScheme.background), modifier = Modifier.background(MaterialTheme.colorScheme.background),
topBar = { topBar = {
val maxTopInset = Dimensions.maxTopBarInset.toLocalPixel()
var topInset by remember { mutableStateOf(maxTopInset) }
snapshotFlow { topAppBarState.collapsedFraction }
.onEach {
topInset = maxTopInset * (1 - it)
}.launchIn(scope)
TopAppBar( TopAppBar(
windowInsets =
if (settings.edgeToEdge) {
WindowInsets(0, topInset.roundToInt(), 0, 0)
} else {
TopAppBarDefaults.windowInsets
},
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
navigationIcon = { navigationIcon = {
if (navigationCoordinator.canPop.value) {
Image( Image(
modifier = modifier =
Modifier.onClick( Modifier
.onClick(
onClick = { onClick = {
navigationCoordinator.popScreen() navigationCoordinator.popScreen()
}, },
@ -174,6 +197,21 @@ class FilteredContentsScreen(
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground), colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
) )
} else {
Image(
modifier =
Modifier.onClick(
onClick = {
scope.launch {
drawerCoordinator.toggleDrawer()
}
},
),
imageVector = Icons.Default.Menu,
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
)
}
}, },
title = { title = {
Column(modifier = Modifier.padding(horizontal = Spacing.s)) { Column(modifier = Modifier.padding(horizontal = Spacing.s)) {
@ -252,21 +290,25 @@ class FilteredContentsScreen(
} }
}, },
) { padding -> ) { padding ->
Column( Box(
modifier = modifier =
Modifier Modifier
.padding( .padding(
top = padding.calculateTopPadding(), top = padding.calculateTopPadding(),
) ).then(
.then(
if (settings.hideNavigationBarWhileScrolling) { if (settings.hideNavigationBarWhileScrolling) {
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
} else { } else {
Modifier Modifier
}, },
), ).nestedScroll(fabNestedScrollConnection)
verticalArrangement = Arrangement.spacedBy(Spacing.s), .pullRefresh(pullRefreshState),
) { ) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = lazyListState,
) {
item {
if (!uiState.isPostOnly) { if (!uiState.isPostOnly) {
SectionSelector( SectionSelector(
titles = titles =
@ -285,29 +327,16 @@ class FilteredContentsScreen(
1 -> FilteredContentsSection.Comments 1 -> FilteredContentsSection.Comments
else -> FilteredContentsSection.Posts else -> FilteredContentsSection.Posts
} }
model.reduce(FilteredContentsMviModel.Intent.ChangeSection(section)) model.reduce(
FilteredContentsMviModel.Intent.ChangeSection(
section,
),
)
}, },
) )
} }
}
Box(
modifier =
Modifier
.fillMaxWidth()
.then(
if (settings.hideNavigationBarWhileScrolling) {
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
Modifier
},
)
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = lazyListState,
) {
if (uiState.section == FilteredContentsSection.Posts) { if (uiState.section == FilteredContentsSection.Posts) {
if (uiState.posts.isEmpty() && uiState.loading && uiState.initial) { if (uiState.posts.isEmpty() && uiState.loading && uiState.initial) {
items(5) { items(5) {
@ -588,17 +617,23 @@ class FilteredContentsScreen(
OptionId.FeaturePost -> OptionId.FeaturePost ->
model.reduce( model.reduce(
FilteredContentsMviModel.Intent.ModFeaturePost(post.id), FilteredContentsMviModel.Intent.ModFeaturePost(
post.id,
),
) )
OptionId.AdminFeaturePost -> OptionId.AdminFeaturePost ->
model.reduce( model.reduce(
FilteredContentsMviModel.Intent.AdminFeaturePost(post.id), FilteredContentsMviModel.Intent.AdminFeaturePost(
post.id,
),
) )
OptionId.LockPost -> OptionId.LockPost ->
model.reduce( model.reduce(
FilteredContentsMviModel.Intent.ModLockPost(post.id), FilteredContentsMviModel.Intent.ModLockPost(
post.id,
),
) )
OptionId.Remove -> { OptionId.Remove -> {
@ -811,13 +846,17 @@ class FilteredContentsScreen(
onUpVote = onUpVote =
rememberCallback(model) { rememberCallback(model) {
model.reduce( model.reduce(
FilteredContentsMviModel.Intent.UpVoteComment(comment.id), FilteredContentsMviModel.Intent.UpVoteComment(
comment.id,
),
) )
}, },
onDownVote = onDownVote =
rememberCallback(model) { rememberCallback(model) {
model.reduce( model.reduce(
FilteredContentsMviModel.Intent.DownVoteComment(comment.id), FilteredContentsMviModel.Intent.DownVoteComment(
comment.id,
),
) )
}, },
onSave = onSave =
@ -988,7 +1027,6 @@ class FilteredContentsScreen(
contentColor = MaterialTheme.colorScheme.onBackground, contentColor = MaterialTheme.colorScheme.onBackground,
) )
} }
}
if (rawContent != null) { if (rawContent != null) {
when (val content = rawContent) { when (val content = rawContent) {