feat: configurable bottom navigation • part 1 (#1111)
This commit is contained in:
parent
3a1b04d010
commit
e8199ed40f
@ -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 {
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user