use experimental rail
This commit is contained in:
+4
@@ -39,3 +39,7 @@ fun createNavigationList(): List<NavigationEntry<Route>> {
|
|||||||
)
|
)
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun List<NavigationEntry<*>>.findTitle(route: Route): String? {
|
||||||
|
return firstOrNull { it.route == route }?.title
|
||||||
|
}
|
||||||
|
|||||||
+44
-95
@@ -1,147 +1,96 @@
|
|||||||
package ru.shadowsparky.vbox.shared.presentation
|
package ru.shadowsparky.vbox.shared.presentation
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.LargeTopAppBar
|
import androidx.compose.material3.LargeTopAppBar
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import ru.shadowsparky.ui.BaseScreen
|
import ru.shadowsparky.ui.BaseScreen
|
||||||
import ru.shadowsparky.ui.LocalBlurState
|
import ru.shadowsparky.ui.LocalBlurState
|
||||||
import ru.shadowsparky.ui.USE_EXPERIMENTAL_RAIL
|
|
||||||
import ru.shadowsparky.ui.applyBlurEffect
|
import ru.shadowsparky.ui.applyBlurEffect
|
||||||
import ru.shadowsparky.ui.components.isTv
|
|
||||||
import ru.shadowsparky.ui.currentWindowType
|
import ru.shadowsparky.ui.currentWindowType
|
||||||
import ru.shadowsparky.ui.isCompact
|
import ru.shadowsparky.ui.isCompact
|
||||||
import ru.shadowsparky.ui.isExpanded
|
|
||||||
import ru.shadowsparky.ui.rememberBlurState
|
import ru.shadowsparky.ui.rememberBlurState
|
||||||
import ru.shadowsparky.ui.theme.ComponentDefaults
|
import ru.shadowsparky.ui.theme.ComponentDefaults
|
||||||
import ru.shadowsparky.vbox.shared.presentation.list.VideoContent
|
import ru.shadowsparky.vbox.shared.presentation.list.VideoContent
|
||||||
import ru.shadowsparky.vbox.shared.presentation.nav.Child
|
import ru.shadowsparky.vbox.shared.presentation.nav.Child
|
||||||
import ru.shadowsparky.vbox.shared.presentation.nav.Route
|
import ru.shadowsparky.vbox.shared.presentation.nav.Route
|
||||||
import ru.shadowsparky.vbox.shared.presentation.saved.SavedMovieContent
|
import ru.shadowsparky.vbox.shared.presentation.saved.SavedMovieScreen
|
||||||
import ru.shadowsparky.vbox.shared.presentation.search.SearchAppBar
|
import ru.shadowsparky.vbox.shared.presentation.search.SearchScreen
|
||||||
import ru.shadowsparky.vbox.shared.presentation.search.SearchVideoContent
|
|
||||||
import ru.shadowsparky.vbox.shared.presentation.settings.SettingsContent
|
import ru.shadowsparky.vbox.shared.presentation.settings.SettingsContent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(
|
fun MainScreen(route: Route, child: Child, rootComponent: RootComponent) {
|
||||||
route: Route,
|
when (child) {
|
||||||
child: Child,
|
is Child.SearchChild -> SearchScreen(child.component, route) { rootComponent.nav(it, true) }
|
||||||
rootComponent: RootComponent
|
is Child.SavedMovieChild -> SavedMovieScreen(child.component, route) { rootComponent.navReplace(it) }
|
||||||
) {
|
else -> MainScreenInternal(route, child) { rootComponent.navReplace(it) }
|
||||||
if (child is Child.SearchChild) {
|
|
||||||
SearchScreenInternal(child, route, rootComponent)
|
|
||||||
} else {
|
|
||||||
MainScreenInternal(route, child, rootComponent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun SearchScreenInternal(
|
fun BaseMainScreen(
|
||||||
child: Child.SearchChild,
|
appBar: @Composable () -> Unit,
|
||||||
route: Route,
|
route: Route,
|
||||||
rootComponent: RootComponent,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
|
onNavigate: (Route) -> Unit,
|
||||||
|
content: @Composable (PaddingValues) -> Unit
|
||||||
) {
|
) {
|
||||||
val component = child.component
|
|
||||||
var textFieldValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
|
||||||
mutableStateOf(TextFieldValue())
|
|
||||||
}
|
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
|
||||||
val requester by remember { mutableStateOf(FocusRequester()) }
|
|
||||||
val windowType = currentWindowType()
|
|
||||||
val searchAppBar: (@Composable () -> Unit) = {
|
|
||||||
SearchAppBar(
|
|
||||||
textFieldValue = textFieldValue,
|
|
||||||
onDone = { component.done(textFieldValue.text) },
|
|
||||||
onValueChange = { textFieldValue = it },
|
|
||||||
focusRequester = requester
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val blurState = rememberBlurState()
|
val blurState = rememberBlurState()
|
||||||
CompositionLocalProvider(LocalBlurState provides blurState) {
|
CompositionLocalProvider(LocalBlurState provides blurState) {
|
||||||
BaseScreen(
|
BaseScreen(
|
||||||
appBar = {
|
appBar = { appBar() },
|
||||||
if (windowType.isCompact || !USE_EXPERIMENTAL_RAIL) searchAppBar()
|
|
||||||
},
|
|
||||||
currentRoute = route,
|
currentRoute = route,
|
||||||
onSelectNavigation = { rootComponent.nav(it, true) },
|
onSelectNavigation = { onNavigate(it) },
|
||||||
navigationEntries = createNavigationList(),
|
navigationEntries = createNavigationList(),
|
||||||
colorScheme = getColorScheme(),
|
colorScheme = getColorScheme(),
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
) {
|
) { padding ->
|
||||||
val content: @Composable () -> Unit = {
|
content(padding)
|
||||||
SearchVideoContent(
|
|
||||||
child.component,
|
|
||||||
it,
|
|
||||||
requester
|
|
||||||
) { textFieldValue = it }
|
|
||||||
}
|
|
||||||
if (windowType.isExpanded && USE_EXPERIMENTAL_RAIL) {
|
|
||||||
Column {
|
|
||||||
searchAppBar()
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreenInternal(route: Route, child: Child, rootComponent: RootComponent) {
|
private fun MainScreenInternal(route: Route, child: Child, onNavigate: (Route) -> Unit) {
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
|
||||||
val navList = createNavigationList()
|
val navList = createNavigationList()
|
||||||
val windowType = currentWindowType()
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||||
val blurState = rememberBlurState()
|
BaseMainScreen(
|
||||||
CompositionLocalProvider(LocalBlurState provides blurState) {
|
appBar = {
|
||||||
BaseScreen(
|
navList.findTitle(route)?.let { title ->
|
||||||
appBar = {
|
if (currentWindowType().isCompact) {
|
||||||
if (windowType.isCompact || !USE_EXPERIMENTAL_RAIL) {
|
LargeTopAppBar(
|
||||||
navList.firstOrNull { it.route == route }?.title?.let {
|
title = { Text(title) },
|
||||||
if (isTv()) {
|
scrollBehavior = scrollBehavior,
|
||||||
TopAppBar(
|
modifier = Modifier.applyBlurEffect(LocalBlurState.current),
|
||||||
title = { Text(it) },
|
colors = ComponentDefaults.topAppBarColors()
|
||||||
scrollBehavior = scrollBehavior,
|
)
|
||||||
modifier = Modifier.applyBlurEffect(blurState),
|
} else {
|
||||||
colors = ComponentDefaults.topAppBarColors()
|
TopAppBar(
|
||||||
)
|
title = { Text(title) },
|
||||||
} else {
|
scrollBehavior = scrollBehavior,
|
||||||
LargeTopAppBar(
|
modifier = Modifier.applyBlurEffect(LocalBlurState.current),
|
||||||
title = { Text(it) },
|
colors = ComponentDefaults.topAppBarColors()
|
||||||
scrollBehavior = scrollBehavior,
|
)
|
||||||
modifier = Modifier.applyBlurEffect(blurState),
|
|
||||||
colors = ComponentDefaults.topAppBarColors()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
currentRoute = route,
|
},
|
||||||
onSelectNavigation = { rootComponent.navReplace(it) },
|
route = route,
|
||||||
navigationEntries = navList,
|
scrollBehavior = scrollBehavior,
|
||||||
colorScheme = getColorScheme(),
|
onNavigate = onNavigate,
|
||||||
scrollBehavior = scrollBehavior
|
content = { padding ->
|
||||||
) { padding ->
|
|
||||||
when (child) {
|
when (child) {
|
||||||
is Child.VideosChild -> VideoContent(child.component, padding)
|
is Child.VideosChild -> VideoContent(child.component, padding)
|
||||||
is Child.SettingsChild -> SettingsContent(child.component, padding)
|
is Child.SettingsChild -> SettingsContent(child.component, padding)
|
||||||
is Child.SavedMovieChild -> SavedMovieContent(child.component, padding)
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-23
@@ -10,7 +10,6 @@ import androidx.compose.foundation.border
|
|||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
@@ -42,7 +41,6 @@ import androidx.compose.ui.graphics.Brush
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@@ -119,7 +117,6 @@ fun VideoScreenContent(
|
|||||||
emptyResource: StringResource,
|
emptyResource: StringResource,
|
||||||
onRefresh: () -> Unit,
|
onRefresh: () -> Unit,
|
||||||
onShowOverview: (Long) -> Unit,
|
onShowOverview: (Long) -> Unit,
|
||||||
extraFab: (@Composable BoxScope.() -> Unit)? = null,
|
|
||||||
onLoad: () -> Unit
|
onLoad: () -> Unit
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
@@ -130,7 +127,7 @@ fun VideoScreenContent(
|
|||||||
onRefresh()
|
onRefresh()
|
||||||
}
|
}
|
||||||
} else if (items != null) {
|
} else if (items != null) {
|
||||||
VideosInfo(items = items, paddingValues, emptyResource, extraFab) {
|
VideosInfo(items = items, paddingValues, emptyResource) {
|
||||||
onShowOverview(it.id)
|
onShowOverview(it.id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -143,16 +140,11 @@ fun VideosInfo(
|
|||||||
items: List<VideoItem>,
|
items: List<VideoItem>,
|
||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
emptyResource: StringResource,
|
emptyResource: StringResource,
|
||||||
extraFab: (@Composable BoxScope.() -> Unit)? = null,
|
|
||||||
onClick: (item: VideoItem) -> Unit
|
onClick: (item: VideoItem) -> Unit
|
||||||
) {
|
) {
|
||||||
val blur = LocalBlurState.current
|
val blur = LocalBlurState.current
|
||||||
val gridState = rememberLazyGridState()
|
val gridState = rememberLazyGridState()
|
||||||
val density = LocalDensity.current
|
|
||||||
val layoutDirection = LocalLayoutDirection.current
|
val layoutDirection = LocalLayoutDirection.current
|
||||||
|
|
||||||
var fabHeight by remember { mutableStateOf(0.dp) }
|
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
if (items.isNotEmpty()) {
|
if (items.isNotEmpty()) {
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
@@ -164,7 +156,7 @@ fun VideosInfo(
|
|||||||
start = paddingValues.calculateStartPadding(layoutDirection),
|
start = paddingValues.calculateStartPadding(layoutDirection),
|
||||||
top = paddingValues.calculateTopPadding(),
|
top = paddingValues.calculateTopPadding(),
|
||||||
end = paddingValues.calculateEndPadding(layoutDirection),
|
end = paddingValues.calculateEndPadding(layoutDirection),
|
||||||
bottom = paddingValues.calculateBottomPadding() + (fabHeight / 2)
|
bottom = paddingValues.calculateBottomPadding()
|
||||||
),
|
),
|
||||||
state = gridState
|
state = gridState
|
||||||
) {
|
) {
|
||||||
@@ -179,19 +171,6 @@ fun VideosInfo(
|
|||||||
} else {
|
} else {
|
||||||
VideosNotFound(emptyResource)
|
VideosNotFound(emptyResource)
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootBoxScope = this
|
|
||||||
|
|
||||||
extraFab?.let {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.align(Alignment.BottomEnd)
|
|
||||||
.onGloballyPositioned { coordinates ->
|
|
||||||
fabHeight = with(density) {
|
|
||||||
coordinates.size.height.toDp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) { it(rootBoxScope) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+39
-22
@@ -9,18 +9,19 @@ import androidx.compose.foundation.clickable
|
|||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.calculateEndPadding
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FloatingActionButton
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -28,19 +29,24 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import ru.shadowsparky.ui.components.LazyColumn
|
import ru.shadowsparky.ui.components.LazyColumn
|
||||||
|
import ru.shadowsparky.ui.theme.ComponentDefaults
|
||||||
import ru.shadowsparky.vbox.shared.domain.tag.TagInfo
|
import ru.shadowsparky.vbox.shared.domain.tag.TagInfo
|
||||||
|
import ru.shadowsparky.vbox.shared.presentation.BaseMainScreen
|
||||||
|
import ru.shadowsparky.vbox.shared.presentation.createNavigationList
|
||||||
|
import ru.shadowsparky.vbox.shared.presentation.findTitle
|
||||||
import ru.shadowsparky.vbox.shared.presentation.list.VideoScreenContent
|
import ru.shadowsparky.vbox.shared.presentation.list.VideoScreenContent
|
||||||
|
import ru.shadowsparky.vbox.shared.presentation.nav.Route
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SavedMovieContent(comp: SavedMovieScreenComponent, paddingValues: PaddingValues) {
|
fun SavedMovieScreen(comp: SavedMovieScreenComponent, route: Route, onNavigate: (Route) -> Unit) {
|
||||||
val items by comp.all.collectAsStateWithLifecycle(null)
|
|
||||||
val tags by comp.userTags.collectAsStateWithLifecycle(null)
|
val tags by comp.userTags.collectAsStateWithLifecycle(null)
|
||||||
|
val navList = createNavigationList()
|
||||||
var filterBottomSheetVisible by rememberSaveable { mutableStateOf(false) }
|
var filterBottomSheetVisible by rememberSaveable { mutableStateOf(false) }
|
||||||
if (filterBottomSheetVisible) {
|
if (filterBottomSheetVisible) {
|
||||||
FilterBottomSheet(
|
FilterBottomSheet(
|
||||||
@@ -50,6 +56,33 @@ fun SavedMovieContent(comp: SavedMovieScreenComponent, paddingValues: PaddingVal
|
|||||||
onFilterBy = { comp.filterByTag(it) }
|
onFilterBy = { comp.filterByTag(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
BaseMainScreen(
|
||||||
|
appBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { navList.findTitle(route)?.let { Text(it) } },
|
||||||
|
actions = {
|
||||||
|
IconButton(
|
||||||
|
onClick = { filterBottomSheetVisible = true }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painterResource(Res.drawable.filter_list),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = ComponentDefaults.topAppBarColors()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
route = route,
|
||||||
|
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(),
|
||||||
|
onNavigate = onNavigate,
|
||||||
|
content = { SavedMovieContent(comp, it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SavedMovieContent(comp: SavedMovieScreenComponent, paddingValues: PaddingValues) {
|
||||||
|
val items by comp.all.collectAsStateWithLifecycle(null)
|
||||||
VideoScreenContent(
|
VideoScreenContent(
|
||||||
paddingValues,
|
paddingValues,
|
||||||
null,
|
null,
|
||||||
@@ -57,23 +90,7 @@ fun SavedMovieContent(comp: SavedMovieScreenComponent, paddingValues: PaddingVal
|
|||||||
onRefresh = {},
|
onRefresh = {},
|
||||||
onShowOverview = { comp.showOverview(it) },
|
onShowOverview = { comp.showOverview(it) },
|
||||||
emptyResource = Res.string.saved_movies_not_found,
|
emptyResource = Res.string.saved_movies_not_found,
|
||||||
onLoad = {},
|
onLoad = {}
|
||||||
extraFab = {
|
|
||||||
if (tags?.isNotEmpty() == true) {
|
|
||||||
FloatingActionButton(
|
|
||||||
onClick = { filterBottomSheetVisible = true },
|
|
||||||
modifier = Modifier.padding(
|
|
||||||
end = paddingValues.calculateEndPadding(LocalLayoutDirection.current) + 4.dp,
|
|
||||||
bottom = paddingValues.calculateBottomPadding() + 4.dp
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painterResource(Res.drawable.filter_list),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-2
@@ -35,13 +35,17 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.material3.TextFieldColors
|
import androidx.compose.material3.TextFieldColors
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberSearchBarState
|
import androidx.compose.material3.rememberSearchBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.withFrameNanos
|
import androidx.compose.runtime.withFrameNanos
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -62,7 +66,6 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import ru.shadowsparky.ui.LocalBlurState
|
import ru.shadowsparky.ui.LocalBlurState
|
||||||
@@ -71,7 +74,42 @@ import ru.shadowsparky.ui.applyBlurSource
|
|||||||
import ru.shadowsparky.ui.components.BackButton
|
import ru.shadowsparky.ui.components.BackButton
|
||||||
import ru.shadowsparky.ui.components.ExpressiveLazyColumn
|
import ru.shadowsparky.ui.components.ExpressiveLazyColumn
|
||||||
import ru.shadowsparky.ui.theme.ComponentDefaults
|
import ru.shadowsparky.ui.theme.ComponentDefaults
|
||||||
|
import ru.shadowsparky.vbox.shared.presentation.BaseMainScreen
|
||||||
import ru.shadowsparky.vbox.shared.presentation.imePaddingInternal
|
import ru.shadowsparky.vbox.shared.presentation.imePaddingInternal
|
||||||
|
import ru.shadowsparky.vbox.shared.presentation.nav.Route
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SearchScreen(
|
||||||
|
component: SearchScreenComponent,
|
||||||
|
route: Route,
|
||||||
|
onNavigate: (Route) -> Unit
|
||||||
|
) {
|
||||||
|
val requester by remember { mutableStateOf(FocusRequester()) }
|
||||||
|
var textFieldValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||||
|
mutableStateOf(TextFieldValue())
|
||||||
|
}
|
||||||
|
BaseMainScreen(
|
||||||
|
appBar = {
|
||||||
|
SearchAppBar(
|
||||||
|
textFieldValue = textFieldValue,
|
||||||
|
onDone = { component.done(textFieldValue.text) },
|
||||||
|
onValueChange = { textFieldValue = it },
|
||||||
|
focusRequester = requester
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
SearchVideoContent(
|
||||||
|
component,
|
||||||
|
it,
|
||||||
|
requester
|
||||||
|
) { textFieldValue = it }
|
||||||
|
},
|
||||||
|
route = route,
|
||||||
|
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(),
|
||||||
|
onNavigate = onNavigate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -103,7 +141,7 @@ fun SearchVideoContent(
|
|||||||
Text(
|
Text(
|
||||||
text = stringResource(Res.string.search_empty_now),
|
text = stringResource(Res.string.search_empty_now),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
modifier = Modifier.clickable { },
|
modifier = Modifier.focusRequester(focusRequester).clickable {},
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package ru.shadowsparky.ui
|
|||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.calculateEndPadding
|
||||||
|
import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawing
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
@@ -29,12 +29,13 @@ import androidx.compose.runtime.Immutable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ru.shadowsparky.ui.nav.Route
|
import ru.shadowsparky.ui.nav.Route
|
||||||
import ru.shadowsparky.ui.theme.ComponentDefaults
|
import ru.shadowsparky.ui.theme.ComponentDefaults
|
||||||
import ru.shadowsparky.ui.theme.getColorScheme
|
import ru.shadowsparky.ui.theme.getColorScheme
|
||||||
|
|
||||||
const val USE_EXPERIMENTAL_RAIL = false
|
const val USE_EXPERIMENTAL_RAIL = true
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class NavigationEntry<out T : Route>(
|
data class NavigationEntry<out T : Route>(
|
||||||
@@ -125,9 +126,7 @@ private fun <T : Route> BottomBar(
|
|||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
currentRoute == entry.route,
|
currentRoute == entry.route,
|
||||||
onClick = { onSelectNavigation?.invoke(entry.route) },
|
onClick = { onSelectNavigation?.invoke(entry.route) },
|
||||||
icon = {
|
icon = { Icon(entry.painter, contentDescription = entry.title) },
|
||||||
Icon(entry.painter, contentDescription = entry.title)
|
|
||||||
},
|
|
||||||
label = { Text(entry.title) }
|
label = { Text(entry.title) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -144,23 +143,30 @@ private fun <T : Route> ExpandedContent(
|
|||||||
onSelectNavigation: ((T) -> Unit)?,
|
onSelectNavigation: ((T) -> Unit)?,
|
||||||
content: @Composable ((PaddingValues) -> Unit)
|
content: @Composable ((PaddingValues) -> Unit)
|
||||||
) {
|
) {
|
||||||
Row(Modifier.fillMaxSize().padding(values)) {
|
val topPadding = values.calculateTopPadding()
|
||||||
Spacer(Modifier.width(4.dp))
|
val bottomPadding = values.calculateBottomPadding()
|
||||||
NavigationRail {
|
val startPadding = values.calculateStartPadding(LayoutDirection.Ltr)
|
||||||
|
val endPadding = values.calculateEndPadding(LayoutDirection.Ltr)
|
||||||
|
Row(modifier = Modifier.fillMaxSize().padding(top = topPadding)) {
|
||||||
|
NavigationRail(modifier = Modifier.padding(horizontal = 4.dp)) {
|
||||||
navigationEntries.forEach { entry ->
|
navigationEntries.forEach { entry ->
|
||||||
NavigationRailItem(
|
NavigationRailItem(
|
||||||
selected = currentRoute == entry.route,
|
selected = currentRoute == entry.route,
|
||||||
onClick = { onSelectNavigation?.invoke(entry.route) },
|
onClick = { onSelectNavigation?.invoke(entry.route) },
|
||||||
icon = {
|
icon = { Icon(entry.painter, contentDescription = entry.title) },
|
||||||
Icon(entry.painter, contentDescription = entry.title)
|
|
||||||
},
|
|
||||||
label = { Text(entry.title) }
|
label = { Text(entry.title) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(4.dp))
|
Box(modifier = Modifier.weight(1f)) {
|
||||||
Box(Modifier.weight(1f)) {
|
content(
|
||||||
content(PaddingValues(0.dp))
|
PaddingValues(
|
||||||
|
start = startPadding,
|
||||||
|
top = 0.dp,
|
||||||
|
end = endPadding,
|
||||||
|
bottom = bottomPadding
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user