From 3962ec29da0b7c8e78b1c2572aad8fae7512a19c Mon Sep 17 00:00:00 2001 From: Eugene Date: Sat, 8 Aug 2026 20:34:47 +0300 Subject: [PATCH] rename to appscaffold, remote simpleappbar --- apps/vbox/client/androidApp/build.gradle.kts | 1 - .../vbox/shared/presentation/MainScreen.kt | 22 ++-- .../{BaseScreen.kt => Navigation.kt} | 23 +--- .../shared/presentation/list/VideoScreen.kt | 49 ++++---- .../presentation/loading/LoadingScreen.kt | 4 +- .../offline/OfflineVideoScreen.kt | 11 +- .../presentation/overview/OverviewScreen.kt | 81 ++++++------ .../overview/episode/EpisodeOverviewScreen.kt | 116 +++++++++--------- .../overview/season/SeasonOverviewScreen.kt | 59 ++++----- .../presentation/settings/auth/AuthScreen.kt | 16 ++- .../settings/pass/EditPasswordScreen.kt | 15 ++- .../settings/serv/EditServScreen.kt | 4 +- .../presentation/settings/tag/TagScreen.kt | 9 +- .../ru/shadowsparky/plugin/AndroidLibrary.kt | 6 +- .../ui/{BaseScreen.kt => AppScaffold.kt} | 58 +++++---- .../shadowsparky/ui/components/Expressive.kt | 8 +- 16 files changed, 224 insertions(+), 258 deletions(-) rename apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/{BaseScreen.kt => Navigation.kt} (87%) rename libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/{BaseScreen.kt => AppScaffold.kt} (81%) diff --git a/apps/vbox/client/androidApp/build.gradle.kts b/apps/vbox/client/androidApp/build.gradle.kts index 2169ec4..d2b0fbc 100644 --- a/apps/vbox/client/androidApp/build.gradle.kts +++ b/apps/vbox/client/androidApp/build.gradle.kts @@ -22,7 +22,6 @@ dependencies { implementation(libs.material) implementation(project(":apps:vbox:client:shared")) implementation(project(":apps:vbox:common")) - implementation(project(":libs:base")) implementation(project(":libs:ui")) implementation(project(":libs:http-client")) 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 577f2ff..6caf6d7 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 @@ -4,11 +4,8 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import ru.shadowsparky.ui.AppScaffold -import ru.shadowsparky.ui.LocalBlurState import ru.shadowsparky.ui.components.TopAppBar -import ru.shadowsparky.ui.rememberBlurState import ru.shadowsparky.vbox.shared.presentation.chat.ChatScreen import ru.shadowsparky.vbox.shared.presentation.list.VideoContent import ru.shadowsparky.vbox.shared.presentation.nav.Child @@ -38,18 +35,13 @@ fun BaseMainScreen( onNavigate: (Route) -> Unit, content: @Composable (PaddingValues) -> Unit ) { - val blurState = rememberBlurState() - CompositionLocalProvider(LocalBlurState provides blurState) { - AppScaffold( - appBar = { appBar() }, - currentRoute = route, - onSelectNavigation = { onNavigate(it) }, - navigationEntries = createNavigationList(), - colorScheme = getColorScheme() - ) { padding -> - content(padding) - } - } + AppScaffold( + appBar = { appBar() }, + currentRoute = route, + onSelectNavigation = { onNavigate(it) }, + navigationEntries = createNavigationList(), + colorScheme = getColorScheme() + ) { padding -> content(padding) } } @OptIn(ExperimentalMaterial3Api::class) 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/Navigation.kt similarity index 87% rename from apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/BaseScreen.kt rename to apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/Navigation.kt index ff5f12e..bdb2637 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/Navigation.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.ColorScheme import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -12,8 +11,6 @@ import androidx.compose.runtime.remember 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 @@ -85,25 +82,7 @@ private fun NavInternal(root: VBoxRootComponent) { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SimpleAppBar( - title: String, - doBack: (() -> Unit)?, - modifier: Modifier = Modifier, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { Text(title) }, - navigationIcon = { - doBack?.let { BackButton(it) } - }, - scrollBehavior = scrollBehavior, - modifier = modifier - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun BaseScreen( +fun AppScaffold( scrollBehavior: TopAppBarScrollBehavior? = null, snackbarState: SnackbarHostState? = null, modifier: Modifier = Modifier, 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 6f7aa8b..f16c25c 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 @@ -29,7 +29,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -57,9 +56,8 @@ 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.AppScaffold import ru.shadowsparky.vbox.shared.presentation.search.AppBarWithSearch @OptIn(ExperimentalMaterial3Api::class) @@ -67,30 +65,27 @@ import ru.shadowsparky.vbox.shared.presentation.search.AppBarWithSearch fun VideoScreen(component: VideoScreenComponent) { val error by component.errorCode.collectAsState(null) val items by component.items.collectAsState() - val blurState = rememberBlurState() - CompositionLocalProvider(LocalBlurState provides blurState) { - BaseScreen( - content = { - VideoScreenContent( - it, - error, - items, - onRefresh = { component.load() }, - onShowOverview = { component.showOverview(it) }, - emptyResource = Res.string.not_found - ) - }, - appBar = { - val appName = stringResource(Res.string.recommendations) - val title = component.search ?: appName - AppBarWithSearch( - title, - isDefault = title == appName, - onBack = { component.doBack() } - ) - } - ) - } + AppScaffold( + content = { + VideoScreenContent( + it, + error, + items, + onRefresh = { component.load() }, + onShowOverview = { component.showOverview(it) }, + emptyResource = Res.string.not_found + ) + }, + appBar = { + val appName = stringResource(Res.string.recommendations) + val title = component.search ?: appName + AppBarWithSearch( + title, + isDefault = title == appName, + onBack = { component.doBack() } + ) + } + ) } @Composable 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 ef23d31..2cf8b01 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 @@ -6,7 +6,7 @@ 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.AppScaffold @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -15,7 +15,7 @@ fun LoadingScreen(component: LoadingComponent) { LaunchedEffect(isLoggedIn) { isLoggedIn?.let { component.onLoaded(it) } } - BaseScreen( + AppScaffold( appBar = {}, content = { Loading() } ) diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/offline/OfflineVideoScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/offline/OfflineVideoScreen.kt index 7e0d266..b2716c6 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/offline/OfflineVideoScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/offline/OfflineVideoScreen.kt @@ -28,6 +28,7 @@ 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.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -42,13 +43,13 @@ import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.ui.AppScaffold +import ru.shadowsparky.ui.components.BackButton import ru.shadowsparky.ui.components.ExpressiveLazyColumn import ru.shadowsparky.ui.components.Loading import ru.shadowsparky.vbox.shared.domain.EpisodeOfflineVideo import ru.shadowsparky.vbox.shared.domain.OfflineState import ru.shadowsparky.vbox.shared.domain.OfflineVideo import ru.shadowsparky.vbox.shared.domain.OfflineVideoInfo -import ru.shadowsparky.vbox.shared.presentation.SimpleAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -56,9 +57,11 @@ fun OfflineVideoScreen(component: OfflineVideoComponent) { val info by component.info.collectAsState(null) AppScaffold( appBar = { - SimpleAppBar( - title = stringResource(Res.string.offline_video), - doBack = { component.onBack() } + TopAppBar( + { Text(stringResource(Res.string.offline_video)) }, + navigationIcon = { + BackButton { component.onBack } + } ) } ) { padding -> 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 59e13ce..fc57596 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 @@ -45,7 +45,6 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState @@ -76,9 +75,8 @@ 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.vbox.shared.domain.model.VideoDetails -import ru.shadowsparky.vbox.shared.presentation.BaseScreen +import ru.shadowsparky.vbox.shared.presentation.AppScaffold import ru.shadowsparky.vbox.shared.presentation.getClipboardText import ru.shadowsparky.vbox.shared.presentation.settings.tag.LinkTagBottomSheet @@ -87,50 +85,47 @@ import ru.shadowsparky.vbox.shared.presentation.settings.tag.LinkTagBottomSheet fun OverviewScreen(component: OverviewComponent) { val errorState by component.errorCode.collectAsState(initial = null) val details by component.details.collectAsState() - val blurState = rememberBlurState() var tagsBottomSheetVisible by rememberSaveable { mutableStateOf(false) } - CompositionLocalProvider(LocalBlurState provides blurState) { - BaseScreen( - appBar = { - OverviewAppBar( - component.offlineSupport, - details, - component, - tagsRequest = { tagsBottomSheetVisible = true } + AppScaffold( + appBar = { + OverviewAppBar( + component.offlineSupport, + details, + component, + tagsRequest = { tagsBottomSheetVisible = true } + ) + }, + content = { padding -> + when { + + errorState != null -> ErrorContent( + errorState!!, + modifier = Modifier.fillMaxSize(), + buttonText = stringResource(Res.string.retry) + ) { component.loadDetailsIfNeeded() } + + details != null -> DetailsInfo( + details = details!!, + paddingValues = padding, + onWatchClicked = { component.watch() } ) - }, - content = { padding -> - when { - errorState != null -> ErrorContent( - errorState!!, - modifier = Modifier.fillMaxSize(), - buttonText = stringResource(Res.string.retry) - ) { component.loadDetailsIfNeeded() } - - details != null -> DetailsInfo( - details = details!!, - paddingValues = padding, - onWatchClicked = { component.watch() } - ) - - else -> Loading() - } - - if (tagsBottomSheetVisible) { - val allTags by component.allTags.collectAsState(null) - val movieTags by component.movieTags.collectAsState(null) - LinkTagBottomSheet( - allTags, - movieTags, - { tagsBottomSheetVisible = false }, - { component.showTags() }, - { id, state -> component.updateTag(id, state) } - ) - } + else -> Loading() } - ) - } + + if (tagsBottomSheetVisible) { + val allTags by component.allTags.collectAsState(null) + val movieTags by component.movieTags.collectAsState(null) + LinkTagBottomSheet( + allTags, + movieTags, + { tagsBottomSheetVisible = false }, + { component.showTags() }, + { id, state -> component.updateTag(id, state) } + ) + } + } + ) } @OptIn(ExperimentalMaterial3Api::class) 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 b66f5a7..2e3f84d 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 @@ -28,7 +28,6 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -52,11 +51,10 @@ 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.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.AppScaffold import ru.shadowsparky.vbox.shared.presentation.getClipboardText @OptIn(ExperimentalMaterial3Api::class) @@ -95,74 +93,70 @@ fun EpisodesOverviewContent( commitOffline: (Boolean) -> Unit ) { val snackbarState = remember { SnackbarHostState() } - val blurState = rememberBlurState() var isSaveMode by rememberSaveable { mutableStateOf(false) } - CompositionLocalProvider(LocalBlurState provides blurState) { - BaseScreen( - snackbarState = snackbarState, - content = { padding -> - val clipboardText = getClipboardText() - EpisodesOverviewList( - onLaunch = { showPlayer(it) }, - contentPadding = padding, - errorCode = error, - episodes = episodes, - loadLinks = { refresh() }, - onClipboardAction = { - onClipboard(Clip(clipboardText, it.url)) - }, - onOfflineClicked = onOfflineClicked, - isSaveMode = isSaveMode - ) - }, - appBar = { - TopAppBar( - title = { - Text(stringResource(Res.string.episodes_overview)) - }, - navigationIcon = { - BackButton { - if (isSaveMode) { - commitOffline(true) - isSaveMode = false - } else { - onBack() - } + AppScaffold( + snackbarState = snackbarState, + content = { padding -> + val clipboardText = getClipboardText() + EpisodesOverviewList( + onLaunch = { showPlayer(it) }, + contentPadding = padding, + errorCode = error, + episodes = episodes, + loadLinks = { refresh() }, + onClipboardAction = { + onClipboard(Clip(clipboardText, it.url)) + }, + onOfflineClicked = onOfflineClicked, + isSaveMode = isSaveMode + ) + }, + appBar = { + TopAppBar( + title = { + Text(stringResource(Res.string.episodes_overview)) + }, + navigationIcon = { + BackButton { + if (isSaveMode) { + commitOffline(true) + isSaveMode = false + } else { + onBack() } - }, - actions = { - if (offlineSupport) { - IconButton( - onClick = { - isSaveMode = !isSaveMode - if (isSaveMode) startOffline() else commitOffline(false) - } - ) { + } + }, + actions = { + if (offlineSupport) { + IconButton( + onClick = { + isSaveMode = !isSaveMode + if (isSaveMode) startOffline() else commitOffline(false) + } + ) { + val resource = + if (isSaveMode) Res.drawable.folder_check else Res.drawable.save_as + Icon( + painter = painterResource(resource), + contentDescription = null + ) + } + if (isSaveMode) { + val selectedAll = episodes?.all { it.offlineInfo != null } ?: false + IconButton(onClick = { onOfflineMultiple(!selectedAll) }) { val resource = - if (isSaveMode) Res.drawable.folder_check else Res.drawable.save_as + if (selectedAll) Res.drawable.deselect else Res.drawable.select_all Icon( painter = painterResource(resource), contentDescription = null ) } - if (isSaveMode) { - val selectedAll = episodes?.all { it.offlineInfo != null } ?: false - IconButton(onClick = { onOfflineMultiple(!selectedAll) }) { - val resource = - if (selectedAll) Res.drawable.deselect else Res.drawable.select_all - Icon( - painter = painterResource(resource), - contentDescription = null - ) - } - } } } - ) - } - ) - } - + } + ) + } + ) } @Composable 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 9cfc6b6..08edb70 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 @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember @@ -28,43 +27,39 @@ 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.vbox.shared.domain.model.Season -import ru.shadowsparky.vbox.shared.presentation.BaseScreen +import ru.shadowsparky.vbox.shared.presentation.AppScaffold @OptIn(ExperimentalMaterial3Api::class) @Composable fun SeasonOverviewScreen(component: SeasonOverviewComponent) { - val blurState = rememberBlurState() - CompositionLocalProvider(LocalBlurState provides blurState) { - BaseScreen( - content = { - val seasons = component.season.collectAsState(null) - val err = component.error.collectAsState(null) - if (err.value != null) { - ErrorContent( - err.value!!, - modifier = Modifier.fillMaxSize(), - buttonText = stringResource(Res.string.retry) - ) { component.load() } - } else if (seasons.value != null) { - SeasonsInfo( - seasons.value!!, it - ) { seasonId -> component.showEpisode(seasonId) } - } else { - Loading() - } - }, appBar = { - TopAppBar( - title = { - Text(stringResource(Res.string.seasons_overview)) - }, navigationIcon = { - BackButton { component.doBack() } - } - ) + AppScaffold( + content = { + val seasons = component.season.collectAsState(null) + val err = component.error.collectAsState(null) + if (err.value != null) { + ErrorContent( + err.value!!, + modifier = Modifier.fillMaxSize(), + buttonText = stringResource(Res.string.retry) + ) { component.load() } + } else if (seasons.value != null) { + SeasonsInfo( + seasons.value!!, it + ) { seasonId -> component.showEpisode(seasonId) } + } else { + Loading() } - ) - } + }, appBar = { + TopAppBar( + title = { + Text(stringResource(Res.string.seasons_overview)) + }, navigationIcon = { + BackButton { component.doBack() } + } + ) + } + ) } @Composable 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 ac7d40b..ffb0906 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 @@ -19,6 +19,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.OutlinedButton 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.MutableState @@ -34,14 +35,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.BackButton 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.AppScaffold @Composable fun AuthScreen(comp: AuthComponent) { @@ -92,8 +93,15 @@ fun BaseLogin( else -> {} } } - BaseScreen( - appBar = { SimpleAppBar(title, doBack) }, + AppScaffold( + appBar = { + TopAppBar( + title = { Text(title) }, + navigationIcon = { + doBack?.let { BackButton { it() } } + } + ) + }, content = { if (state is AuthState.Loading) { Loading() 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 4f785eb..bc2e7cc 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 @@ -19,6 +19,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -31,10 +32,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.BackButton 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.AppScaffold import ru.shadowsparky.vbox.shared.presentation.settings.auth.AuthState @OptIn(ExperimentalMaterial3Api::class) @@ -43,11 +44,13 @@ fun EditPasswordScreen(component: EditPasswordComponent) { val state by component.state.collectAsState() val snackbarState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() - BaseScreen( + AppScaffold( appBar = { - SimpleAppBar( - stringResource(Res.string.edit_password), - doBack = { component.onBack() } + TopAppBar( + { Text(stringResource(Res.string.edit_password)) }, + navigationIcon = { + BackButton { component.onBack() } + } ) }, snackbarState = snackbarState, 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 6076f8c..8ac6ac4 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 @@ -37,7 +37,7 @@ 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.AppScaffold @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable @@ -49,7 +49,7 @@ fun EditServScreen(comp: EditServComponent) { } val keyboardController = LocalSoftwareKeyboardController.current val focusRequester = remember { FocusRequester() } - BaseScreen( + AppScaffold( appBar = { TopAppBar( title = { Text(stringResource(Res.string.edit_custom_serv_addr)) }, diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/tag/TagScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/tag/TagScreen.kt index 3ef7a0b..2c4e9db 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/tag/TagScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/settings/tag/TagScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -35,10 +36,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import ru.shadowsparky.ui.AppScaffold import ru.shadowsparky.ui.components.AlertContent +import ru.shadowsparky.ui.components.BackButton import ru.shadowsparky.ui.components.ExpressiveLazyColumn import ru.shadowsparky.ui.components.TextPreference import ru.shadowsparky.vbox.shared.domain.tag.TagInfo -import ru.shadowsparky.vbox.shared.presentation.SimpleAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -48,9 +49,9 @@ fun TagScreen(component: TagComponent) { var activeTag by remember { mutableStateOf(null) } AppScaffold( appBar = { - SimpleAppBar( - stringResource(Res.string.user_tags), - component.onBack + TopAppBar( + { Text(stringResource(Res.string.user_tags)) }, + navigationIcon = { BackButton { component.onBack() } } ) } ) { diff --git a/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidLibrary.kt b/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidLibrary.kt index 24d7630..a9d01c7 100644 --- a/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidLibrary.kt +++ b/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidLibrary.kt @@ -5,6 +5,7 @@ import com.android.build.api.dsl.LibraryExtension import org.gradle.api.Action import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware import org.gradle.kotlin.dsl.configure import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import ru.shadowsparky.applyCoroutines @@ -37,6 +38,5 @@ class AndroidLibrary : Plugin { } } -private fun org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension.android(configure: Action): Unit = - (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("android", configure) - +private fun KotlinMultiplatformExtension.android(configure: Action): Unit = + (this as ExtensionAware).extensions.configure("android", configure) diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/BaseScreen.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/AppScaffold.kt similarity index 81% rename from libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/BaseScreen.kt rename to libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/AppScaffold.kt index b384d03..45ab286 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/BaseScreen.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/AppScaffold.kt @@ -27,6 +27,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush @@ -80,35 +81,38 @@ fun AppScaffold( content: @Composable (padding: PaddingValues) -> Unit, ) { val windowType = currentWindowType() - MaterialExpressiveTheme(colorScheme = colorScheme) { - Scaffold( - bottomBar = { - if (windowType.isCompact) { - BottomBar(navigationEntries, currentRoute, onSelectNavigation) - } - }, - topBar = { - appBar(scrollBehavior) - }, content = { - if (windowType.isExpanded && navigationEntries?.isNotEmpty() == true) { - ExpandedContent( - it, - navigationEntries, - currentRoute, - onSelectNavigation, - content - ) + val blurState = rememberBlurState() + CompositionLocalProvider(LocalBlurState provides blurState) { + MaterialExpressiveTheme(colorScheme = colorScheme) { + Scaffold( + bottomBar = { + if (windowType.isCompact) { + BottomBar(navigationEntries, currentRoute, onSelectNavigation) + } + }, + topBar = { + appBar(scrollBehavior) + }, content = { + if (windowType.isExpanded && navigationEntries?.isNotEmpty() == true) { + ExpandedContent( + it, + navigationEntries, + currentRoute, + onSelectNavigation, + content + ) + } else { + content(it) + } + }, modifier = if (scrollBehavior != null) { + modifier.nestedScroll(scrollBehavior.nestedScrollConnection) } else { - content(it) + modifier + }, snackbarHost = { + snackbarState?.let { SnackbarHost(it) } } - }, modifier = if (scrollBehavior != null) { - modifier.nestedScroll(scrollBehavior.nestedScrollConnection) - } else { - modifier - }, snackbarHost = { - snackbarState?.let { SnackbarHost(it) } - } - ) + ) + } } } diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Expressive.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Expressive.kt index 772c152..d41d2b6 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Expressive.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/components/Expressive.kt @@ -117,8 +117,8 @@ fun ExpressiveLazyColumn( item(key = node.key, contentType = node.contentType) { ExpressiveCard( - topRadius = topRadius, - bottomRadius = bottomRadius, + topRadius = if (reverseLayout) bottomRadius else topRadius, + bottomRadius = if (reverseLayout) topRadius else bottomRadius, horizontalPadding = horizontalPadding, verticalSpacing = verticalSpacing, modifier = Modifier.animateItem( @@ -126,9 +126,7 @@ fun ExpressiveLazyColumn( fadeOutSpec = spring(), placementSpec = spring() ) - ) { - node.content(this@item) - } + ) { node.content(this@item) } } } }