diff --git a/apps/vbox/client/androidApp/src/main/kotlin/ru/shadowsparky/videobox/multiplatform/preview/VideoScreenPreview.kt b/apps/vbox/client/androidApp/src/main/kotlin/ru/shadowsparky/videobox/multiplatform/preview/VideoScreenPreview.kt deleted file mode 100644 index 6c00d37..0000000 --- a/apps/vbox/client/androidApp/src/main/kotlin/ru/shadowsparky/videobox/multiplatform/preview/VideoScreenPreview.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ru.shadowsparky.videobox.multiplatform.preview - -import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview -import ru.shadowsparky.vbox.shared.presentation.list.ErrorAndRetry -import ru.shadowsparky.vbox.shared.presentation.list.Loading - -@Preview -@Composable -fun ErrorPreview() { - ErrorAndRetry( - errorMsg = "Неизвестная ошибка" - ) {} -} - -@Preview -@Composable -fun LoadingPreview() { - Loading() -} diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/BaseScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/BaseScreen.kt index f99972a..ff5f12e 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/BaseScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/BaseScreen.kt @@ -5,7 +5,6 @@ import androidx.compose.material3.ColorScheme import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -14,6 +13,7 @@ import androidx.compose.ui.Modifier import com.seiko.imageloader.LocalImageLoader import ru.shadowsparky.koin import ru.shadowsparky.ui.components.BackButton +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.ui.nav.Nav import ru.shadowsparky.updater.presentation.UpdateDialog import ru.shadowsparky.vbox.shared.presentation.list.VideoScreen diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/MainScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/MainScreen.kt index 2efceaf..577f2ff 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/MainScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/MainScreen.kt @@ -3,17 +3,12 @@ package ru.shadowsparky.vbox.shared.presentation import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier import ru.shadowsparky.ui.AppScaffold import ru.shadowsparky.ui.LocalBlurState -import ru.shadowsparky.ui.applyBlurEffect +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.ui.rememberBlurState -import ru.shadowsparky.ui.theme.ComponentDefaults import ru.shadowsparky.vbox.shared.presentation.chat.ChatScreen import ru.shadowsparky.vbox.shared.presentation.list.VideoContent import ru.shadowsparky.vbox.shared.presentation.nav.Child @@ -40,7 +35,6 @@ fun MainScreen(route: Route, child: Child, root: VBoxRootComponent) { fun BaseMainScreen( appBar: @Composable () -> Unit, route: Route, - scrollBehavior: TopAppBarScrollBehavior, onNavigate: (Route) -> Unit, content: @Composable (PaddingValues) -> Unit ) { @@ -51,8 +45,7 @@ fun BaseMainScreen( currentRoute = route, onSelectNavigation = { onNavigate(it) }, navigationEntries = createNavigationList(), - colorScheme = getColorScheme(), - scrollBehavior = scrollBehavior + colorScheme = getColorScheme() ) { padding -> content(padding) } @@ -63,20 +56,15 @@ fun BaseMainScreen( @Composable private fun MainScreenInternal(route: Route, child: Child, onNavigate: (Route) -> Unit) { val navList = createNavigationList() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() BaseMainScreen( appBar = { navList.findTitle(route)?.let { title -> TopAppBar( - title = { Text(title) }, - scrollBehavior = scrollBehavior, - modifier = Modifier.applyBlurEffect(LocalBlurState.current), - colors = ComponentDefaults.topAppBarColors() + title = { Text(title) } ) } }, route = route, - scrollBehavior = scrollBehavior, onNavigate = onNavigate, content = { padding -> when (child) { diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/chat/ChatScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/chat/ChatScreen.kt index eea2267..20cba00 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/chat/ChatScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/chat/ChatScreen.kt @@ -4,18 +4,13 @@ import androiddev.apps.vbox.client.shared.generated.resources.Res import androiddev.apps.vbox.client.shared.generated.resources.chat_title_app_bar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.subscribeAsState import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.chat.presentation.ChatComponent import ru.shadowsparky.chat.presentation.ChatContent -import ru.shadowsparky.ui.LocalBlurState -import ru.shadowsparky.ui.applyBlurEffect -import ru.shadowsparky.ui.theme.ComponentDefaults +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.vbox.shared.presentation.BaseMainScreen import ru.shadowsparky.vbox.shared.presentation.nav.Route @@ -25,15 +20,10 @@ fun ChatScreen(component: ChatComponent, route: Route, onNavigate: (Route) -> Un val state by component.state.subscribeAsState() BaseMainScreen( appBar = { - TopAppBar( - title = { Text(stringResource(Res.string.chat_title_app_bar)) }, - colors = ComponentDefaults.topAppBarColors(), - modifier = Modifier.applyBlurEffect(LocalBlurState.current) - ) + TopAppBar(title = { Text(stringResource(Res.string.chat_title_app_bar)) }) }, route, onNavigate = onNavigate, - scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(), content = { ChatContent(component, it, state) } ) } diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt index 20dc443..3e1f72d 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize @@ -26,11 +25,9 @@ import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -57,19 +54,17 @@ import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.ui.LocalBlurState import ru.shadowsparky.ui.applyBlurSource +import ru.shadowsparky.ui.components.ErrorContent import ru.shadowsparky.ui.components.LazyVerticalGrid import ru.shadowsparky.ui.components.Loading import ru.shadowsparky.ui.rememberBlurState import ru.shadowsparky.vbox.shared.domain.model.VideoItem import ru.shadowsparky.vbox.shared.presentation.BaseScreen import ru.shadowsparky.vbox.shared.presentation.search.AppBarWithSearch -import ru.shadowsparky.vbox.shared.presentation.state.rememberTopAppBarStateByHolder @OptIn(ExperimentalMaterial3Api::class) @Composable fun VideoScreen(component: VideoScreenComponent) { - val appBarState = rememberTopAppBarStateByHolder(component.topAppBarStateHolder) - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(appBarState) val error by component.errorCode.collectAsState(null) val items by component.items.collectAsState() val blurState = rememberBlurState() @@ -93,8 +88,7 @@ fun VideoScreen(component: VideoScreenComponent) { isDefault = title == appName, onBack = { component.doBack() } ) - }, - scrollBehavior = scrollBehavior + } ) } } @@ -123,9 +117,10 @@ fun VideoScreenContent( onShowOverview: (Long) -> Unit ) { if (error != null) { - ErrorAndRetry(error) { - onRefresh() - } + ErrorContent( + error, + buttonText = stringResource(Res.string.retry) + ) { onRefresh() } } else if (items != null) { AnimatedContent( items, @@ -259,29 +254,6 @@ fun Poster( } } - -@Composable -fun ErrorAndRetry(errorMsg: String, retry: () -> Unit) { - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxSize() - ) { - Text( - text = errorMsg, - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(bottom = 4.dp), - textAlign = TextAlign.Center - ) - Button(onClick = { retry() }) { - Text( - text = stringResource(Res.string.retry), - style = MaterialTheme.typography.bodyMedium - ) - } - } -} - @Composable fun VideosNotFound(emptyResource: StringResource) { Box( @@ -296,8 +268,3 @@ fun VideosNotFound(emptyResource: StringResource) { ) } } - -@Composable -fun Loading() { - Loading(Modifier.fillMaxSize()) -} diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreenComponent.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreenComponent.kt index 8f24741..39c0f11 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreenComponent.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/list/VideoScreenComponent.kt @@ -19,13 +19,11 @@ import ru.shadowsparky.vbox.shared.presentation.createExceptionHandler import ru.shadowsparky.vbox.shared.presentation.nav.Child import ru.shadowsparky.vbox.shared.presentation.nav.Route import ru.shadowsparky.vbox.shared.presentation.nav.RouteIds -import ru.shadowsparky.vbox.shared.presentation.state.TopAppBarStateHolder class VideoScreenComponent( private val context: ComponentContext, private val videoApi: VideoApi, private val dispatcherProvider: DispatcherProvider, - val topAppBarStateHolder: TopAppBarStateHolder, val search: String? = null, val showOverview: (Long) -> Unit, val doBack: () -> Unit @@ -53,7 +51,6 @@ class VideoScreenComponent( @Factory @Named(RouteIds.VIDEOS) class VideosChildFactory( - private val topAppBarStateHolder: TopAppBarStateHolder, private val videoApi: VideoApi, private val dispatcherProvider: DispatcherProvider ) : ComponentFactory { @@ -66,7 +63,6 @@ class VideosChildFactory( child, videoApi, dispatcherProvider, - topAppBarStateHolder, route.request, showOverview = { root.nav(Route.Overview(it)) }, doBack = { root.onBack() } diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/loading/LoadingScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/loading/LoadingScreen.kt index b7962fb..ef23d31 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/loading/LoadingScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/loading/LoadingScreen.kt @@ -5,8 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import ru.shadowsparky.ui.components.Loading import ru.shadowsparky.vbox.shared.presentation.BaseScreen -import ru.shadowsparky.vbox.shared.presentation.list.Loading @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt index 2180ebc..5697e42 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt @@ -8,6 +8,7 @@ import androiddev.apps.vbox.client.shared.generated.resources.details_serial import androiddev.apps.vbox.client.shared.generated.resources.more import androiddev.apps.vbox.client.shared.generated.resources.offline_remove import androiddev.apps.vbox.client.shared.generated.resources.offline_save +import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.share import androiddev.apps.vbox.client.shared.generated.resources.star import androiddev.apps.vbox.client.shared.generated.resources.star_unchecked @@ -41,11 +42,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -72,26 +69,23 @@ import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.domain.Clip import ru.shadowsparky.ui.LocalBlurState -import ru.shadowsparky.ui.applyBlurEffect import ru.shadowsparky.ui.applyBlurSource import ru.shadowsparky.ui.components.BackButton +import ru.shadowsparky.ui.components.ErrorContent import ru.shadowsparky.ui.components.LazyColumn +import ru.shadowsparky.ui.components.Loading +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.ui.rememberBlurState -import ru.shadowsparky.ui.theme.ComponentDefaults import ru.shadowsparky.vbox.shared.domain.model.VideoDetails import ru.shadowsparky.vbox.shared.presentation.BaseScreen import ru.shadowsparky.vbox.shared.presentation.getClipboardText -import ru.shadowsparky.vbox.shared.presentation.list.ErrorAndRetry -import ru.shadowsparky.vbox.shared.presentation.list.Loading import ru.shadowsparky.vbox.shared.presentation.settings.tag.LinkTagBottomSheet @OptIn(ExperimentalMaterial3Api::class) @Composable fun OverviewScreen(component: OverviewComponent) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val errorState by component.errorCode.collectAsState(initial = null) val details by component.details.collectAsState() - val snackbarState = remember { SnackbarHostState() } val blurState = rememberBlurState() var tagsBottomSheetVisible by rememberSaveable { mutableStateOf(false) } CompositionLocalProvider(LocalBlurState provides blurState) { @@ -101,17 +95,15 @@ fun OverviewScreen(component: OverviewComponent) { component.offlineSupport, details, component, - scrollBehavior, tagsRequest = { tagsBottomSheetVisible = true } ) }, - scrollBehavior = scrollBehavior, - snackbarState = snackbarState, content = { padding -> when { - errorState != null -> ErrorAndRetry( - errorMsg = errorState!! + errorState != null -> ErrorContent( + errorState!!, + buttonText = stringResource(Res.string.retry) ) { component.loadDetailsIfNeeded() } details != null -> DetailsInfo( @@ -145,12 +137,10 @@ private fun OverviewAppBar( offlineSupport: Boolean, details: VideoDetails?, component: OverviewComponent, - scrollBehavior: TopAppBarScrollBehavior, tagsRequest: () -> Unit ) { val isSaved by component.isSaved.collectAsStateWithLifecycle(null) val offlineItem by component.offlineItem.collectAsStateWithLifecycle(null) - val blur = LocalBlurState.current TopAppBar( title = { details?.title?.let { @@ -162,7 +152,6 @@ private fun OverviewAppBar( } }, navigationIcon = { BackButton { component.doBack() } }, - colors = ComponentDefaults.topAppBarColors(), actions = { details?.let { if (component.shareManager.canShare()) { @@ -192,9 +181,7 @@ private fun OverviewAppBar( tagsRequest ) { component.updateOffline(offlineItem) } } - }, - scrollBehavior = scrollBehavior, - modifier = Modifier.applyBlurEffect(blur) + } ) } diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt index 4e407b2..eb48870 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/episode/EpisodeOverviewScreen.kt @@ -7,6 +7,7 @@ import androiddev.apps.vbox.client.shared.generated.resources.episodes_overview import androiddev.apps.vbox.client.shared.generated.resources.eye import androiddev.apps.vbox.client.shared.generated.resources.folder_check import androiddev.apps.vbox.client.shared.generated.resources.pending +import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.save_as import androiddev.apps.vbox.client.shared.generated.resources.select_all import androidx.compose.foundation.ExperimentalFoundationApi @@ -26,8 +27,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -47,19 +46,18 @@ import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.domain.Clip import ru.shadowsparky.ui.LocalBlurState -import ru.shadowsparky.ui.applyBlurEffect import ru.shadowsparky.ui.applyBlurSource import ru.shadowsparky.ui.components.BackButton +import ru.shadowsparky.ui.components.ErrorContent import ru.shadowsparky.ui.components.ExpressiveLazyColumn +import ru.shadowsparky.ui.components.Loading +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.ui.rememberBlurState -import ru.shadowsparky.ui.theme.ComponentDefaults import ru.shadowsparky.vbox.shared.domain.OfflineState import ru.shadowsparky.vbox.shared.domain.OfflineVideo import ru.shadowsparky.vbox.shared.domain.model.Episode import ru.shadowsparky.vbox.shared.presentation.BaseScreen import ru.shadowsparky.vbox.shared.presentation.getClipboardText -import ru.shadowsparky.vbox.shared.presentation.list.ErrorAndRetry -import ru.shadowsparky.vbox.shared.presentation.list.Loading @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -96,7 +94,6 @@ fun EpisodesOverviewContent( startOffline: () -> Unit, commitOffline: (Boolean) -> Unit ) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val snackbarState = remember { SnackbarHostState() } val blurState = rememberBlurState() var isSaveMode by rememberSaveable { mutableStateOf(false) } @@ -133,9 +130,6 @@ fun EpisodesOverviewContent( } } }, - scrollBehavior = scrollBehavior, - colors = ComponentDefaults.topAppBarColors(), - modifier = Modifier.applyBlurEffect(blurState), actions = { if (offlineSupport) { IconButton( @@ -165,8 +159,7 @@ fun EpisodesOverviewContent( } } ) - }, - scrollBehavior = scrollBehavior + } ) } @@ -184,7 +177,10 @@ fun EpisodesOverviewList( onOfflineClicked: (EpisodeWithOfflineInfo) -> Unit ) { if (errorCode != null) { - ErrorAndRetry(errorMsg = errorCode) { loadLinks() } + ErrorContent( + errorCode, + buttonText = stringResource(Res.string.retry) + ) { loadLinks() } } else if (episodes != null) { val blur = LocalBlurState.current val requester = remember { FocusRequester() } diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewComponent.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewComponent.kt index f50bdbb..a066b2b 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewComponent.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewComponent.kt @@ -34,6 +34,8 @@ class SeasonOverviewComponent( val error = _error.asSharedFlow() private val exceptionHandler = createExceptionHandler(componentScope) { _error.emit(it) } + init { load() } + fun load() { componentScope.launch(dispatchers.main + exceptionHandler) { _error.emit(null) diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt index f1cee46..cd25ac9 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/season/SeasonOverviewScreen.kt @@ -1,6 +1,7 @@ package ru.shadowsparky.vbox.shared.presentation.overview.season import androiddev.apps.vbox.client.shared.generated.resources.Res +import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.seasons_overview import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.PaddingValues @@ -8,8 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -22,22 +21,19 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.ui.LocalBlurState -import ru.shadowsparky.ui.applyBlurEffect import ru.shadowsparky.ui.applyBlurSource import ru.shadowsparky.ui.components.BackButton +import ru.shadowsparky.ui.components.ErrorContent import ru.shadowsparky.ui.components.ExpressiveLazyColumn +import ru.shadowsparky.ui.components.Loading +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.ui.rememberBlurState -import ru.shadowsparky.ui.theme.ComponentDefaults import ru.shadowsparky.vbox.shared.domain.model.Season import ru.shadowsparky.vbox.shared.presentation.BaseScreen -import ru.shadowsparky.vbox.shared.presentation.list.ErrorAndRetry -import ru.shadowsparky.vbox.shared.presentation.list.Loading @OptIn(ExperimentalMaterial3Api::class) @Composable fun SeasonOverviewScreen(component: SeasonOverviewComponent) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - LaunchedEffect(null) { component.load() } val blurState = rememberBlurState() CompositionLocalProvider(LocalBlurState provides blurState) { BaseScreen( @@ -45,9 +41,10 @@ fun SeasonOverviewScreen(component: SeasonOverviewComponent) { val seasons = component.season.collectAsState(null) val err = component.error.collectAsState(null) if (err.value != null) { - ErrorAndRetry(err.value!!) { - component.load() - } + ErrorContent( + err.value!!, + buttonText = stringResource(Res.string.retry) + ) { component.load() } } else if (seasons.value != null) { SeasonsInfo( seasons.value!!, it @@ -61,11 +58,9 @@ fun SeasonOverviewScreen(component: SeasonOverviewComponent) { Text(stringResource(Res.string.seasons_overview)) }, navigationIcon = { BackButton { component.doBack() } - }, scrollBehavior = scrollBehavior, - colors = ComponentDefaults.topAppBarColors(), - modifier = Modifier.applyBlurEffect(blurState) + } ) - }, scrollBehavior = scrollBehavior + } ) } } diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt index 0ba6eb7..aa44048 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt @@ -2,6 +2,7 @@ package ru.shadowsparky.vbox.shared.presentation.player import androiddev.apps.vbox.client.shared.generated.resources.Res import androiddev.apps.vbox.client.shared.generated.resources.next_episode +import androiddev.apps.vbox.client.shared.generated.resources.retry import androiddev.apps.vbox.client.shared.generated.resources.start_from import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -9,7 +10,6 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape @@ -28,8 +28,8 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.ui.AppScaffold +import ru.shadowsparky.ui.components.ErrorContent import ru.shadowsparky.ui.components.Loading -import ru.shadowsparky.vbox.shared.presentation.list.ErrorAndRetry import ru.shadowsparky.videoplayer.PlayerEvent import ru.shadowsparky.videoplayer.ui.VideoPlayer @@ -47,12 +47,11 @@ fun PlayerScreen(component: PlayerComponent) { when (val state = e.state) { is PlayerEvent.Idle -> Loading(Modifier.fillMaxSize()) is PlayerEvent.Error -> { - Column(modifier = Modifier.fillMaxSize().padding(it)) { - ErrorAndRetry( - state.message, - retry = { component.load() } - ) - } + ErrorContent( + state.message, + modifier = Modifier.padding(it), + buttonText = stringResource(Res.string.retry), + ) { component.load() } } is PlayerEvent.Ended -> component.requestNextEpisode() diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/saved/SavedMovieScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/saved/SavedMovieScreen.kt index 4f603cb..aabac2e 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/saved/SavedMovieScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/saved/SavedMovieScreen.kt @@ -18,8 +18,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -31,11 +29,9 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource -import ru.shadowsparky.ui.LocalBlurState -import ru.shadowsparky.ui.applyBlurEffect import ru.shadowsparky.ui.components.AlertContent import ru.shadowsparky.ui.components.LazyColumn -import ru.shadowsparky.ui.theme.ComponentDefaults +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.vbox.shared.domain.tag.TagInfo import ru.shadowsparky.vbox.shared.presentation.BaseMainScreen import ru.shadowsparky.vbox.shared.presentation.list.VideoScreenContent @@ -59,13 +55,10 @@ fun SavedMovieScreen(comp: SavedMovieComponent, route: Route, onNavigate: (Route contentDescription = null ) } - }, - colors = ComponentDefaults.topAppBarColors(), - modifier = Modifier.applyBlurEffect(LocalBlurState.current) + } ) }, route = route, - scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(), onNavigate = onNavigate, content = { if (filterBottomSheetVisible) { diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt index 5e98160..d24d362 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/search/SearchScreen.kt @@ -103,7 +103,6 @@ fun SearchScreen( ) { textFieldValue = it } }, route = route, - scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(), onNavigate = onNavigate ) } diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/auth/AuthScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/auth/AuthScreen.kt index 925a421..ac7d40b 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/auth/AuthScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/auth/AuthScreen.kt @@ -7,17 +7,13 @@ import androiddev.apps.vbox.client.shared.generated.resources.account_password import androiddev.apps.vbox.client.shared.generated.resources.account_reg_hint import androiddev.apps.vbox.client.shared.generated.resources.empty_login import androiddev.apps.vbox.client.shared.generated.resources.empty_pass -import androiddev.apps.vbox.client.shared.generated.resources.error_title -import androiddev.apps.vbox.client.shared.generated.resources.okay import androiddev.apps.vbox.client.shared.generated.resources.unknown_error -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.OutlinedButton @@ -38,13 +34,14 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.Flow import org.jetbrains.compose.resources.stringResource +import ru.shadowsparky.ui.components.ErrorAlert +import ru.shadowsparky.ui.components.Loading import ru.shadowsparky.ui.components.PasswordTextField import ru.shadowsparky.vbox.shared.domain.model.EmptyLoginException import ru.shadowsparky.vbox.shared.domain.model.EmptyPasswordException import ru.shadowsparky.vbox.shared.domain.model.LoginException import ru.shadowsparky.vbox.shared.presentation.BaseScreen import ru.shadowsparky.vbox.shared.presentation.SimpleAppBar -import ru.shadowsparky.vbox.shared.presentation.list.Loading @Composable fun AuthScreen(comp: AuthComponent) { @@ -107,17 +104,9 @@ fun BaseLogin( modifier = Modifier.padding(8.dp) ) { if (errorInfo != null && state is AuthState.Error) { - AlertDialog( - onDismissRequest = { errorInfo = null }, - title = { Text(stringResource(Res.string.error_title)) }, - text = { Text(errorInfo!!.toMessage()) }, - confirmButton = { - Text( - text = stringResource(Res.string.okay), - modifier = Modifier.padding(8.dp) - .clickable { errorInfo = null } - ) - }, + ErrorAlert( + errorInfo!!.toMessage(), + onClick = { errorInfo = null } ) } OutlinedTextField( diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/pass/EditPasswordScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/pass/EditPasswordScreen.kt index 862c018..4f785eb 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/pass/EditPasswordScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/pass/EditPasswordScreen.kt @@ -31,10 +31,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource +import ru.shadowsparky.ui.components.Loading import ru.shadowsparky.ui.components.PasswordTextField import ru.shadowsparky.vbox.shared.presentation.BaseScreen import ru.shadowsparky.vbox.shared.presentation.SimpleAppBar -import ru.shadowsparky.vbox.shared.presentation.list.Loading import ru.shadowsparky.vbox.shared.presentation.settings.auth.AuthState @OptIn(ExperimentalMaterial3Api::class) diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/serv/EditServScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/serv/EditServScreen.kt index a7aef7a..6076f8c 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/serv/EditServScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/serv/EditServScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -36,21 +35,18 @@ import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.ui.components.BackButton +import ru.shadowsparky.ui.components.Loading +import ru.shadowsparky.ui.components.TopAppBar import ru.shadowsparky.vbox.shared.presentation.BaseScreen -import ru.shadowsparky.vbox.shared.presentation.list.Loading @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable fun EditServScreen(comp: EditServComponent) { val url by comp.serverUrl.collectAsState(null) - LaunchedEffect(null) { - comp.state.collect { - if (it is EditServState.Succeed) { - comp.doBack() - } - } - } val state by comp.state.collectAsState(null) + LaunchedEffect(state) { + if (state is EditServState.Succeed) comp.doBack() + } val keyboardController = LocalSoftwareKeyboardController.current val focusRequester = remember { FocusRequester() } BaseScreen( @@ -98,5 +94,6 @@ fun EditServScreen(comp: EditServComponent) { } else { Loading() } - }) + } + ) } diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/state/TopAppBarStateHolder.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/state/TopAppBarStateHolder.kt deleted file mode 100644 index 1878de0..0000000 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/state/TopAppBarStateHolder.kt +++ /dev/null @@ -1,35 +0,0 @@ -package ru.shadowsparky.vbox.shared.presentation.state - -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.TopAppBarState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.saveable.rememberSaveable -import org.koin.core.annotation.Factory - -@Factory -class TopAppBarStateHolder { - var heightOffsetLimit: Float = -Float.MAX_VALUE - var heightOffset: Float = 0f - var contentOffset: Float = 0f -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun rememberTopAppBarStateByHolder(holder: TopAppBarStateHolder): TopAppBarState { - val state = rememberSaveable(saver = TopAppBarState.Saver) { - TopAppBarState( - holder.heightOffsetLimit, - holder.heightOffset, - holder.contentOffset - ) - } - DisposableEffect(Unit) { - onDispose { - holder.heightOffsetLimit = state.heightOffsetLimit - holder.heightOffset = state.heightOffset - holder.contentOffset = state.contentOffset - } - } - return state -} diff --git a/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt b/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt index c8a5e68..2627b06 100644 --- a/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt +++ b/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt @@ -92,9 +92,7 @@ fun ChatContent( } is ChatState.Error -> { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( text = stringResource(Res.string.chat_load_error), color = MaterialTheme.colorScheme.error diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a374128..4e48e4a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jun 23 20:20:20 MSK 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/libs/ui/src/commonMain/composeResources/values/strings.xml b/libs/ui/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 0000000..c05ccd3 --- /dev/null +++ b/libs/ui/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,4 @@ + + Произошла ошибка + Понятно + diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/ErrorInfo.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/ErrorInfo.kt new file mode 100644 index 0000000..be6e3ac --- /dev/null +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/ErrorInfo.kt @@ -0,0 +1,93 @@ +package ru.shadowsparky.ui.components + +import androiddev.libs.ui.generated.resources.Res +import androiddev.libs.ui.generated.resources.error_title +import androiddev.libs.ui.generated.resources.ok +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.jetbrains.compose.resources.stringResource + +@Composable +fun ErrorContent( + message: String, + modifier: Modifier = Modifier, + buttonModifier: Modifier = Modifier, + buttonText: String = stringResource(Res.string.ok), + onClick: () -> Unit +) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier.fillMaxSize().padding(4.dp) + ) { + Text( + text = stringResource(Res.string.error_title), + style = MaterialTheme.typography.titleMedium, + textAlign = TextAlign.Center + ) + Spacer(Modifier.height(2.dp)) + Text( + text = message, + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center + ) + Spacer(Modifier.height(8.dp)) + Button(modifier = buttonModifier, onClick = { onClick() }) { + Text( + text = buttonText, + style = MaterialTheme.typography.bodyMedium + ) + } + } +} + +@Composable +fun ErrorAlert( + message: String, + buttonText: String = stringResource(Res.string.ok), + onClick: () -> Unit, + onSkip: () -> Unit = onClick +) { + AlertContent(onSkip) { + ErrorContent( + message, + Modifier, + Modifier.fillMaxWidth(), + buttonText, + onClick + ) + } +} + +@Preview +@Composable +fun ErrorContentPreview() { + ErrorContent("что-то пошло не так") { + + } +} + +@Preview +@Composable +fun ErrorAlertPreview() { + ErrorAlert( + "Увы... Что-то опять идет не так...", + onClick = { + + } + ) +} diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyColumn.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyColumn.kt index 971248c..1fb48ff 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyColumn.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyColumn.kt @@ -14,6 +14,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import ru.shadowsparky.ui.LocalBlurState +import ru.shadowsparky.ui.applyBlurSource @Composable expect fun isTv(): Boolean @@ -123,7 +125,11 @@ fun LazyColumn( ) { val isTv = isTv() androidx.compose.foundation.lazy.LazyColumn( - if (isTv) modifier.padding(contentPadding) else modifier, + if (isTv) { + modifier.padding(contentPadding) + } else { + modifier.applyBlurSource(LocalBlurState.current) + }, state, if (isTv) PaddingValues(0.dp) else contentPadding, reverseLayout, diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyVerticalGrid.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyVerticalGrid.kt index b0fa6ab..abfb1c0 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyVerticalGrid.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/LazyVerticalGrid.kt @@ -14,6 +14,8 @@ import androidx.compose.foundation.rememberOverscrollEffect import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import ru.shadowsparky.ui.LocalBlurState +import ru.shadowsparky.ui.applyBlurSource @Composable fun LazyVerticalGrid( @@ -33,7 +35,11 @@ fun LazyVerticalGrid( val isTv = isTv() androidx.compose.foundation.lazy.grid.LazyVerticalGrid( columns, - if (isTv) modifier.padding(contentPadding) else modifier, + if (isTv) { + modifier.padding(contentPadding) + } else { + modifier.applyBlurSource(LocalBlurState.current) + }, state, if (isTv) PaddingValues(0.dp) else contentPadding, reverseLayout, diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Loading.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Loading.kt index fa1d18b..278bc03 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Loading.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Loading.kt @@ -1,6 +1,7 @@ package ru.shadowsparky.ui.components import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.LoadingIndicator @@ -12,7 +13,7 @@ import androidx.compose.ui.unit.dp @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun Loading(modifier: Modifier) { +fun Loading(modifier: Modifier = Modifier.fillMaxSize()) { Box(modifier, contentAlignment = Alignment.Center) { LoadingIndicator(Modifier.size(96.dp)) } diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/TopAppBar.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/TopAppBar.kt new file mode 100644 index 0000000..395f347 --- /dev/null +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/TopAppBar.kt @@ -0,0 +1,42 @@ +package ru.shadowsparky.ui.components + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import ru.shadowsparky.ui.LocalBlurState +import ru.shadowsparky.ui.applyBlurEffect +import ru.shadowsparky.ui.theme.ComponentDefaults + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TopAppBar( + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + expandedHeight: Dp = TopAppBarDefaults.TopAppBarExpandedHeight, + windowInsets: WindowInsets = TopAppBarDefaults.windowInsets, + colors: TopAppBarColors = ComponentDefaults.topAppBarColors(), + scrollBehavior: TopAppBarScrollBehavior? = null, + contentPadding: PaddingValues = TopAppBarDefaults.ContentPadding +) { + TopAppBar( + title = title, + modifier = modifier.applyBlurEffect(LocalBlurState.current), + navigationIcon = navigationIcon, + actions = actions, + expandedHeight = expandedHeight, + windowInsets = windowInsets, + colors = colors, + scrollBehavior = scrollBehavior, + contentPadding = contentPadding + ) +}