mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-11-04 10:13:59 +00:00
refactor: replace currentWindowAdaptiveInfo with currentWindowWidthBreakpoint for adaptive layouts
This commit is contained in:
parent
bb9f225fd7
commit
caad76ba95
@ -16,13 +16,18 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="queue_title">Queue</string>
|
||||
<string name="queue_clear_queue">Clear Queue</string>
|
||||
<string name="queue_filter_queue">Filter queue...</string>
|
||||
<string name="queue_no_queue_entries">No queue entries</string>
|
||||
|
||||
<string name="settings_screen_title">Settings</string>
|
||||
|
||||
<string name="settings_section_plugins">Plugins</string>
|
||||
<string name="settings_section_language_region">Language and Region</string>
|
||||
<string name="settings_section_appearance">Appearance</string>
|
||||
<string name="settings_section_playback">Playback</string>
|
||||
<string name="settings_section_downloads">Downloads</string>
|
||||
<string name="settings_section_downloads">Downloads</string>f
|
||||
<string name="settings_section_caching">Caching</string>
|
||||
<string name="settings_section_desktop">Desktop</string>
|
||||
<string name="settings_section_updates">Updates</string>
|
||||
|
||||
@ -76,7 +76,8 @@ interface NavigationItem {
|
||||
|
||||
private val tabs = listOf<NavigationItem>(
|
||||
NavigationItem.Tab<Any>("Home", Iconsax.IconsaxHome, Routes.Home),
|
||||
NavigationItem.Tab<Any>("Search", Iconsax.IconsaxSearch, Routes.Search))
|
||||
NavigationItem.Tab<Any>("Search", Iconsax.IconsaxSearch, Routes.Search)
|
||||
)
|
||||
|
||||
val sidebarTabs = tabs + listOf(
|
||||
NavigationItem.Group(
|
||||
@ -89,7 +90,8 @@ val sidebarTabs = tabs + listOf(
|
||||
LibraryTab.Playlists
|
||||
),
|
||||
NavigationItem.Tab("Albums", Iconsax.IconsaxCd, Routes.Library, LibraryTab.Albums),
|
||||
NavigationItem.Tab("Artists", Iconsax.User, Routes.Library, LibraryTab.Artists))
|
||||
NavigationItem.Tab("Artists", Iconsax.User, Routes.Library, LibraryTab.Artists)
|
||||
)
|
||||
),
|
||||
NavigationItem.Group(
|
||||
"On Device",
|
||||
@ -105,23 +107,23 @@ val sidebarTabs = tabs + listOf(
|
||||
Iconsax.IconsaxDirectboxReceive,
|
||||
Routes.Library,
|
||||
LibraryTab.Downloads
|
||||
))
|
||||
)
|
||||
)
|
||||
),
|
||||
NavigationItem.Group(
|
||||
"Connect",
|
||||
items = listOf(
|
||||
NavigationItem.Tab("Devices", Iconsax.IconsaxMirrorScreenRegular, Routes.Devices),
|
||||
NavigationItem.Tab("Group Jam", Iconsax.IconsaxSoundTwotone, Routes.Jam))))
|
||||
NavigationItem.Tab("Group Jam", Iconsax.IconsaxSoundTwotone, Routes.Jam)
|
||||
)
|
||||
)
|
||||
)
|
||||
val bottombarTabs = tabs + listOf(
|
||||
NavigationItem.Tab<Any>("Library", Iconsax.IconsaxMusicLibrary, Routes.Library),
|
||||
NavigationItem.Tab("Settings", Iconsax.IconsaxSetting2, Routes.Settings)
|
||||
)
|
||||
|
||||
@OptIn(
|
||||
KoinExperimentalAPI::class,
|
||||
ExperimentalCoroutinesApi::class,
|
||||
ExperimentalSharedTransitionApi::class
|
||||
)
|
||||
@OptIn(KoinExperimentalAPI::class)
|
||||
@Composable
|
||||
fun App(
|
||||
content: @Composable () -> Unit = {}
|
||||
@ -156,7 +158,8 @@ fun App(
|
||||
AppShell(navigator, navigationState) {
|
||||
SharedTransitionLayout {
|
||||
CompositionLocalProvider(
|
||||
LocalSharedTransitionScope provides this@SharedTransitionLayout) {
|
||||
LocalSharedTransitionScope provides this@SharedTransitionLayout
|
||||
) {
|
||||
Column {
|
||||
NavDisplay(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
||||
@ -32,16 +32,14 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.rememberModalBottomSheetState
|
||||
import org.spotube.millennium.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.krtirtho.spotube.resources.iconsax.FluentDismiss
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||
@ -51,11 +49,9 @@ fun AdaptiveDialogBottomSheet(
|
||||
onDismiss: () -> Unit,
|
||||
title: @Composable (() -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
breakpointDp: Float = 600f,
|
||||
actions: @Composable (() -> Unit)? = null,
|
||||
content: @Composable () -> Unit) {
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val isLargeScreen = adaptiveInfo.windowSizeClass.minWidthDp >= breakpointDp
|
||||
val isLargeScreen = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Medium
|
||||
|
||||
if (isLargeScreen) {
|
||||
MillenniumDialog(
|
||||
|
||||
@ -18,17 +18,8 @@
|
||||
package dev.krtirtho.spotube.core.ui.component
|
||||
|
||||
|
||||
import org.spotube.millennium.composables.MillenniumIcon
|
||||
import org.spotube.millennium.composables.MillenniumIconButton
|
||||
import org.spotube.millennium.composables.MillenniumIconButtonStyle
|
||||
import org.spotube.millennium.composables.MillenniumModalBottomSheet
|
||||
import org.spotube.millennium.composables.MillenniumSeparator
|
||||
import org.spotube.millennium.composables.MillenniumTextField
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.hoverable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
@ -42,23 +33,9 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import com.composeunstyled.DropdownMenuPanel
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.Text
|
||||
import com.composeunstyled.UnstyledDropdownMenu
|
||||
import com.composeunstyled.UnstyledDropdownMenuItem
|
||||
import com.composeunstyled.rememberModalBottomSheetState
|
||||
import org.spotube.millennium.adaptive.currentWindowAdaptiveInfo
|
||||
import org.spotube.millennium.theme.MillenniumTheme
|
||||
import org.spotube.millennium.theme.gelBrush
|
||||
import org.spotube.millennium.theme.millenniumPanelBrush
|
||||
import org.spotube.millennium.theme.millenniumSurface
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -69,21 +46,37 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.composeunstyled.DropdownMenuPanel
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.Text
|
||||
import com.composeunstyled.UnstyledDropdownMenu
|
||||
import com.composeunstyled.UnstyledDropdownMenuItem
|
||||
import com.composeunstyled.rememberModalBottomSheetState
|
||||
import compose.icons.FeatherIcons
|
||||
import compose.icons.feathericons.Check
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax3DotsMore
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxFilterSearch
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxHeart
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxShare
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxTrash
|
||||
import org.spotube.millennium.composables.MillenniumIcon
|
||||
import org.spotube.millennium.composables.MillenniumIconButton
|
||||
import org.spotube.millennium.composables.MillenniumIconButtonStyle
|
||||
import org.spotube.millennium.composables.MillenniumModalBottomSheet
|
||||
import org.spotube.millennium.composables.MillenniumSeparator
|
||||
import org.spotube.millennium.composables.MillenniumTextField
|
||||
import org.spotube.millennium.theme.MillenniumTheme
|
||||
import org.spotube.millennium.theme.gelBrush
|
||||
import org.spotube.millennium.theme.millenniumPanelBrush
|
||||
import org.spotube.millennium.theme.millenniumSurface
|
||||
|
||||
data class AdaptiveMenuItem(
|
||||
val icon: ImageVector? = null,
|
||||
@ -106,14 +99,12 @@ fun AdaptiveDropdownBottomSheet(
|
||||
items: List<AdaptiveMenuItem>,
|
||||
trigger: @Composable (onClick: () -> Unit) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
breakpointDp: Float = 600f,
|
||||
menuMinWidth: Dp = 200.dp,
|
||||
header: @Composable (() -> Unit)? = null,
|
||||
headerDisplayMode: HeaderDisplayMode = HeaderDisplayMode.Always,
|
||||
filter: ((AdaptiveMenuItem, String) -> Boolean)? = null) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val isLargeScreen = adaptiveInfo.windowSizeClass.minWidthDp >= breakpointDp
|
||||
val isLargeScreen = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Medium
|
||||
|
||||
Box(modifier = modifier) {
|
||||
if (isLargeScreen) {
|
||||
|
||||
@ -31,7 +31,8 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@ -89,8 +90,8 @@ fun CollectionDetails(
|
||||
showFollowButton: Boolean = true,
|
||||
onEdit: (() -> Unit)? = null,
|
||||
sharedElementKey: String? = null) {
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
val isCompact = maxWidth < 600.dp
|
||||
Box(modifier = modifier.fillMaxWidth()) {
|
||||
val isCompact = currentWindowWidthBreakpoint() isBelow WindowBreakpoints.Medium
|
||||
|
||||
val sharedTransitionScope = LocalSharedTransitionScope.current
|
||||
val animatedVisibilityScope = LocalAnimatedVisibilityScope.current
|
||||
|
||||
@ -41,7 +41,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import com.composeunstyled.Text
|
||||
import org.spotube.millennium.adaptive.currentWindowAdaptiveInfo
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
@ -104,8 +105,7 @@ private fun FeaturedCarouselContent(
|
||||
val centerIndex = items.size / 2
|
||||
var selectedIndex by remember { mutableIntStateOf(centerIndex) }
|
||||
val state = rememberCoverflowState(centerIndex) { index -> selectedIndex = index }
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val isSmallScreen = adaptiveInfo.windowSizeClass.minWidthDp <= 600
|
||||
val isSmallScreen = currentWindowWidthBreakpoint() isBelow WindowBreakpoints.Medium
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
|
||||
@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.spotube.core.ui.component
|
||||
|
||||
|
||||
import org.spotube.millennium.composables.MillenniumButton
|
||||
import org.spotube.millennium.composables.MillenniumButtonStyle
|
||||
import org.spotube.millennium.composables.MillenniumIcon
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
||||
import com.composeunstyled.LocalTextStyle
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import com.composeunstyled.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* The shared navigation item used by every sidebar in the app (the main app sidebar
|
||||
* and the settings sidebar), so they stay visually identical.
|
||||
*
|
||||
* @param icon leading icon of the item.
|
||||
* @param badge optional overlay drawn on the icon (for example a download indicator).
|
||||
*/
|
||||
@Composable
|
||||
fun SidebarItem(
|
||||
label: String,
|
||||
icon: ImageVector,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
expanded: Boolean = true,
|
||||
badge: (@Composable BoxScope.() -> Unit)? = null) {
|
||||
val itemContent: @Composable () -> Unit = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically) {
|
||||
Box(modifier = Modifier.size(16.dp)) {
|
||||
MillenniumIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = label,
|
||||
tint = if (selected) {
|
||||
AppTheme.colors.primary
|
||||
} else {
|
||||
AppTheme.colors.onSurfaceVariant
|
||||
})
|
||||
badge?.invoke(this)
|
||||
}
|
||||
AnimatedVisibility(visible = expanded, enter = fadeIn(), exit = fadeOut()) {
|
||||
Text(
|
||||
text = label,
|
||||
singleLine = true,
|
||||
style = LocalTextStyle.current.copy(
|
||||
fontSize = AppTheme.typography.labelMedium.fontSize,
|
||||
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Medium,
|
||||
color = if (selected) {
|
||||
AppTheme.colors.onSecondaryContainer
|
||||
} else {
|
||||
AppTheme.colors.onSurfaceVariant
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = if (expanded) Modifier.weight(1f) else Modifier)
|
||||
}
|
||||
}
|
||||
|
||||
val buttonModifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 7.dp, vertical = 2.dp)
|
||||
|
||||
if (selected) {
|
||||
MillenniumButton(
|
||||
onClick = onClick,
|
||||
modifier = buttonModifier,
|
||||
style = MillenniumButtonStyle.Secondary,
|
||||
content = itemContent)
|
||||
} else {
|
||||
MillenniumButton(
|
||||
onClick = onClick,
|
||||
modifier = buttonModifier,
|
||||
style = MillenniumButtonStyle.Quiet,
|
||||
content = itemContent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The shared section heading used above groups of [SidebarItem]s.
|
||||
*/
|
||||
@Composable
|
||||
fun SidebarSectionLabel(
|
||||
text: String,
|
||||
expanded: Boolean = true) {
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
Text(
|
||||
text = text.uppercase(),
|
||||
style = AppTheme.typography.labelSmall,
|
||||
color = AppTheme.colors.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 8.dp))
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,9 @@
|
||||
package dev.krtirtho.spotube.core.ui.component
|
||||
|
||||
|
||||
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
@ -78,7 +81,6 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@ -207,11 +209,10 @@ fun TrackList(
|
||||
}
|
||||
}
|
||||
|
||||
val windowInfo = LocalWindowInfo.current
|
||||
val maxWidth = with(density) { windowInfo.containerSize.width.toDp() }
|
||||
val isCompact = maxWidth < CompactTrackListBreakpoint
|
||||
val widthBreakpoint = currentWindowWidthBreakpoint()
|
||||
val isCompact = widthBreakpoint isBelow WindowBreakpoints.Medium
|
||||
val isDesktop = remember { getPlatform().isDesktop() }
|
||||
val showIndex = maxWidth >= LargeTrackListBreakpoint
|
||||
val showIndex = widthBreakpoint isAtLeast WindowBreakpoints.Expanded
|
||||
val showAlbum = !isCompact
|
||||
val useDropdownForOptions = isDesktop && !isCompact
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import androidx.compose.ui.unit.sp
|
||||
|
||||
import dev.krtirtho.spotube.modules.settings.Theme
|
||||
import dev.krtirtho.spotube.modules.settings.UserSettings
|
||||
import org.spotube.millennium.composables.MillenniumTooltipHost
|
||||
import org.spotube.millennium.theme.MillenniumColors
|
||||
import org.spotube.millennium.theme.MillenniumPalette
|
||||
import org.spotube.millennium.theme.MillenniumTheme
|
||||
@ -75,7 +76,8 @@ data class AppColors(
|
||||
val onError: Color,
|
||||
val errorContainer: Color,
|
||||
val onErrorContainer: Color,
|
||||
val scrim: Color)
|
||||
val scrim: Color
|
||||
)
|
||||
|
||||
/** Maps Millennium's semantic palette onto the app's role names. */
|
||||
fun appColorsFrom(m: MillenniumColors): AppColors = AppColors(
|
||||
@ -106,7 +108,8 @@ fun appColorsFrom(m: MillenniumColors): AppColors = AppColors(
|
||||
onError = m.background,
|
||||
errorContainer = m.danger.copy(alpha = 0.16f),
|
||||
onErrorContainer = m.danger,
|
||||
scrim = m.scrim)
|
||||
scrim = m.scrim
|
||||
)
|
||||
|
||||
/** Named text styles used by the app, independent of Material typography. */
|
||||
@Immutable
|
||||
@ -123,16 +126,19 @@ data class AppTypography(
|
||||
val bodySmall: TextStyle,
|
||||
val labelLarge: TextStyle,
|
||||
val labelMedium: TextStyle,
|
||||
val labelSmall: TextStyle)
|
||||
val labelSmall: TextStyle
|
||||
)
|
||||
|
||||
private fun style(
|
||||
size: Int,
|
||||
lineHeight: Int,
|
||||
weight: FontWeight = FontWeight.Normal): TextStyle = TextStyle(
|
||||
weight: FontWeight = FontWeight.Normal
|
||||
): TextStyle = TextStyle(
|
||||
fontFamily = FontFamily.SansSerif,
|
||||
fontSize = size.sp,
|
||||
lineHeight = lineHeight.sp,
|
||||
fontWeight = weight)
|
||||
fontWeight = weight
|
||||
)
|
||||
|
||||
val AppDefaultTypography = AppTypography(
|
||||
displaySmall = style(36, 44),
|
||||
@ -147,21 +153,25 @@ val AppDefaultTypography = AppTypography(
|
||||
bodySmall = style(12, 16),
|
||||
labelLarge = style(14, 20, FontWeight.Medium),
|
||||
labelMedium = style(12, 16, FontWeight.Medium),
|
||||
labelSmall = style(11, 16, FontWeight.Medium))
|
||||
labelSmall = style(11, 16, FontWeight.Medium)
|
||||
)
|
||||
|
||||
/** Corner shapes used by the app. */
|
||||
@Immutable
|
||||
data class AppShapes(
|
||||
val small: Shape,
|
||||
val medium: Shape,
|
||||
val large: Shape)
|
||||
val large: Shape
|
||||
)
|
||||
|
||||
val AppDefaultShapes = AppShapes(
|
||||
small = RoundedCornerShape(8.dp),
|
||||
medium = RoundedCornerShape(12.dp),
|
||||
large = RoundedCornerShape(16.dp))
|
||||
large = RoundedCornerShape(16.dp)
|
||||
)
|
||||
|
||||
val LocalAppColors = staticCompositionLocalOf { appColorsFrom(org.spotube.millennium.theme.MillenniumLightColors) }
|
||||
val LocalAppColors =
|
||||
staticCompositionLocalOf { appColorsFrom(org.spotube.millennium.theme.MillenniumLightColors) }
|
||||
val LocalAppTypography = staticCompositionLocalOf { AppDefaultTypography }
|
||||
val LocalAppShapes = staticCompositionLocalOf { AppDefaultShapes }
|
||||
|
||||
@ -182,7 +192,8 @@ object AppTheme {
|
||||
@Composable
|
||||
fun SpotubeTheme(
|
||||
settings: UserSettings,
|
||||
content: @Composable () -> Unit) {
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val isDarkTheme = when (settings.theme) {
|
||||
Theme.LIGHT -> false
|
||||
Theme.DARK -> true
|
||||
@ -192,11 +203,14 @@ fun SpotubeTheme(
|
||||
|
||||
MillenniumTheme(dark = isDarkTheme, config = MillenniumThemeConfig(palette = palette)) {
|
||||
val colors = appColorsFrom(MillenniumTheme.colors)
|
||||
CompositionLocalProvider(
|
||||
LocalAppColors provides colors,
|
||||
LocalAppTypography provides AppDefaultTypography,
|
||||
LocalAppShapes provides AppDefaultShapes,
|
||||
content = content)
|
||||
MillenniumTooltipHost {
|
||||
CompositionLocalProvider(
|
||||
LocalAppColors provides colors,
|
||||
LocalAppTypography provides AppDefaultTypography,
|
||||
LocalAppShapes provides AppDefaultShapes,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,8 @@ import org.spotube.millennium.theme.MillenniumTheme
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@ -327,8 +328,8 @@ private fun ArtistHeaderCard(
|
||||
onFollowClick: () -> Unit,
|
||||
isBlacklisted: Boolean,
|
||||
onBlacklistClick: () -> Unit) {
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
|
||||
val isCompact = maxWidth < 600.dp
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
val isCompact = currentWindowWidthBreakpoint() isBelow WindowBreakpoints.Medium
|
||||
|
||||
MillenniumCard(
|
||||
modifier = Modifier
|
||||
|
||||
@ -42,7 +42,8 @@ import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.Text
|
||||
import com.composeunstyled.rememberModalBottomSheetState
|
||||
import org.spotube.millennium.adaptive.currentWindowAdaptiveInfo
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -74,12 +75,10 @@ fun AddToPlaylistPicker(
|
||||
onDismiss: () -> Unit,
|
||||
onPlaylistSelected: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
libraryRepository: LibraryRepository = koinInject(),
|
||||
breakpointDp: Float = 600f) {
|
||||
libraryRepository: LibraryRepository = koinInject()) {
|
||||
if (!visible) return
|
||||
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val isLargeScreen = adaptiveInfo.windowSizeClass.minWidthDp >= breakpointDp
|
||||
val isLargeScreen = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Medium
|
||||
|
||||
var playlists by remember { mutableStateOf<List<MetadataPlaylist>>(emptyList()) }
|
||||
var isLoading by remember { mutableStateOf(true) }
|
||||
|
||||
@ -41,7 +41,8 @@ import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.Text
|
||||
import com.composeunstyled.rememberModalBottomSheetState
|
||||
import org.spotube.millennium.adaptive.currentWindowAdaptiveInfo
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -82,12 +83,10 @@ fun PlaylistFormSheet(
|
||||
onSubmit: (PlaylistFormData) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
initialData: PlaylistFormData? = null,
|
||||
isEditing: Boolean = false,
|
||||
breakpointDp: Float = 600f) {
|
||||
isEditing: Boolean = false) {
|
||||
if (!visible) return
|
||||
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val isLargeScreen = adaptiveInfo.windowSizeClass.minWidthDp >= breakpointDp
|
||||
val isLargeScreen = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Medium
|
||||
|
||||
var name by remember(initialData) { mutableStateOf(initialData?.name ?: "") }
|
||||
var description by remember(initialData) { mutableStateOf(initialData?.description ?: "") }
|
||||
|
||||
@ -44,7 +44,8 @@ import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.Text
|
||||
import com.composeunstyled.rememberModalBottomSheetState
|
||||
import org.spotube.millennium.adaptive.currentWindowAdaptiveInfo
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -79,12 +80,10 @@ fun AddTracksToPlaylistDialog(
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
searchRepository: SearchRepository = koinInject(),
|
||||
libraryRepository: LibraryRepository = koinInject(),
|
||||
breakpointDp: Float = 600f) {
|
||||
libraryRepository: LibraryRepository = koinInject()) {
|
||||
if (!visible) return
|
||||
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val isLargeScreen = adaptiveInfo.windowSizeClass.minWidthDp >= breakpointDp
|
||||
val isLargeScreen = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Medium
|
||||
|
||||
var query by remember { mutableStateOf("") }
|
||||
var searchResults by remember { mutableStateOf<List<MetadataTrack>>(emptyList()) }
|
||||
|
||||
@ -17,8 +17,10 @@
|
||||
|
||||
package dev.krtirtho.spotube.modules.settings
|
||||
|
||||
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@ -67,8 +69,8 @@ private val SettingsDialogMinWidth = 720.dp
|
||||
fun SettingsScreen(settingsViewModel: SettingsViewModel) {
|
||||
val navigatorCommands: NavigationCommands = koinInject()
|
||||
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
||||
if (maxWidth >= SettingsDialogMinWidth) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
if (currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Expanded) {
|
||||
SettingsDialog(
|
||||
settingsViewModel = settingsViewModel,
|
||||
onDismiss = { navigatorCommands.pop(Routes.Settings) })
|
||||
|
||||
@ -23,6 +23,7 @@ import org.spotube.millennium.composables.MillenniumIconButton
|
||||
import org.spotube.millennium.composables.MillenniumIconButtonStyle
|
||||
import org.spotube.millennium.composables.MillenniumIndeterminateProgress
|
||||
import org.spotube.millennium.composables.MillenniumSeparator
|
||||
import org.spotube.millennium.composables.MillenniumSidebarItem
|
||||
import org.spotube.millennium.theme.MillenniumTheme
|
||||
import org.spotube.millennium.theme.millenniumPanelBrush
|
||||
import org.spotube.millennium.theme.millenniumSurface
|
||||
@ -62,7 +63,6 @@ import dev.krtirtho.spotube.PlatformType
|
||||
import dev.krtirtho.spotube.getPlatform
|
||||
import dev.krtirtho.spotube.core.discovery.rememberLocalNetworkPermissionRequester
|
||||
import dev.krtirtho.spotube.core.navigation.NavigationCommands
|
||||
import dev.krtirtho.spotube.core.ui.component.SidebarItem
|
||||
import dev.krtirtho.spotube.modules.blacklist.BlacklistContent
|
||||
import dev.krtirtho.spotube.modules.plugin.PluginScreenContent
|
||||
import dev.krtirtho.spotube.modules.settings.sections.appearanceSection
|
||||
@ -247,11 +247,12 @@ private fun SettingsSidebar(
|
||||
)
|
||||
}
|
||||
items(tabs, key = { it.name }) { tab ->
|
||||
SidebarItem(
|
||||
MillenniumSidebarItem(
|
||||
label = stringResource(tab.titleRes),
|
||||
icon = tab.icon,
|
||||
onClick = { onSelect(tab) },
|
||||
selected = tab == selectedTab,
|
||||
onClick = { onSelect(tab) })
|
||||
contentDescription = stringResource(tab.titleRes),
|
||||
icon = tab.icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,11 @@
|
||||
package dev.krtirtho.spotube.modules.settings.components
|
||||
|
||||
|
||||
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.ui.Alignment
|
||||
import org.spotube.millennium.composables.MillenniumIcon
|
||||
import org.spotube.millennium.composables.MillenniumButton
|
||||
@ -27,7 +32,6 @@ import org.spotube.millennium.composables.MillenniumRadioGroup
|
||||
import org.spotube.millennium.composables.MillenniumSwitch
|
||||
import org.spotube.millennium.composables.MillenniumTextField
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@ -92,8 +96,8 @@ internal fun <T> SelectionSettingCard(
|
||||
filter: ((AdaptiveMenuItem, String) -> Boolean)? = null) {
|
||||
var isDialogOpen by remember { mutableStateOf(false) }
|
||||
|
||||
BoxWithConstraints {
|
||||
val isWideLayout = maxWidth >= inlineSelectorMinWidth
|
||||
Box {
|
||||
val isWideLayout = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Medium
|
||||
|
||||
SettingCardItem(
|
||||
title = title,
|
||||
@ -173,8 +177,8 @@ internal fun TextInputSettingCard(
|
||||
enabled: Boolean = true) {
|
||||
var isDialogOpen by remember { mutableStateOf(false) }
|
||||
|
||||
BoxWithConstraints {
|
||||
val isWideLayout = maxWidth >= inlineTextFieldMinWidth
|
||||
Box {
|
||||
val isWideLayout = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Medium
|
||||
val inlineState = rememberTextFieldState(value)
|
||||
|
||||
LaunchedEffect(inlineState, isWideLayout) {
|
||||
|
||||
@ -23,8 +23,6 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.BoxWithConstraintsScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
@ -32,43 +30,29 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import org.spotube.millennium.composables.MillenniumSnackbarHost
|
||||
import org.spotube.millennium.composables.MillenniumSnackbarHostState
|
||||
import org.spotube.millennium.composables.MillenniumSeparator
|
||||
import org.spotube.millennium.theme.MillenniumTheme
|
||||
import com.composeunstyled.Sheet
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.UnstyledBottomSheet
|
||||
import com.composeunstyled.rememberBottomSheetState
|
||||
import com.composeunstyled.Text
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.chrisbanes.haze.hazeSource
|
||||
import dev.chrisbanes.haze.rememberHazeState
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.composeunstyled.Sheet
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.UnstyledBottomSheet
|
||||
import com.composeunstyled.rememberBottomSheetState
|
||||
import dev.chrisbanes.haze.hazeSource
|
||||
import dev.chrisbanes.haze.rememberHazeState
|
||||
import dev.krtirtho.spotube.core.navigation.NavigationCommands
|
||||
import dev.krtirtho.spotube.core.navigation.NavigationState
|
||||
import dev.krtirtho.spotube.core.navigation.Navigator
|
||||
@ -86,10 +70,13 @@ import dev.krtirtho.spotube.modules.shell.player_queue.QueueSheet
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.compose.koinInject
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.spotube.millennium.composables.MillenniumSeparator
|
||||
import org.spotube.millennium.composables.MillenniumSnackbarHost
|
||||
import org.spotube.millennium.composables.MillenniumSnackbarHostState
|
||||
import org.spotube.millennium.theme.MillenniumTheme
|
||||
|
||||
val LocalAppShellBottomInset = staticCompositionLocalOf { 0.dp }
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun AppShell(
|
||||
navigator: Navigator,
|
||||
@ -158,7 +145,6 @@ fun AppShell(
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Row(modifier = Modifier.weight(1f)) {
|
||||
AppSidebar(
|
||||
hazeState = bgHazeState,
|
||||
navigator = navigator,
|
||||
navigationState = navigationState
|
||||
)
|
||||
@ -214,7 +200,7 @@ fun AppShell(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@ -278,7 +264,7 @@ fun AppShell(
|
||||
@OptIn(
|
||||
ExperimentalSharedTransitionApi::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun BoxWithConstraintsScope.CompactPlayerOverlay(
|
||||
private fun CompactPlayerOverlay(
|
||||
navigator: Navigator,
|
||||
navigationState: NavigationState,
|
||||
viewModel: AppShellViewModel,
|
||||
|
||||
@ -17,9 +17,10 @@
|
||||
|
||||
package dev.krtirtho.spotube.modules.shell
|
||||
|
||||
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -63,9 +64,7 @@ class AppShellViewModel : ViewModel() {
|
||||
if (maxWidth != null) {
|
||||
return maxWidth >= sidebarMinWidth
|
||||
}
|
||||
val density = LocalDensity.current
|
||||
val windowInfo = LocalWindowInfo.current
|
||||
return with(density) { windowInfo.containerSize.width.toDp() >= sidebarMinWidth }
|
||||
return currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Expanded
|
||||
}
|
||||
|
||||
fun bottomOverlayInset(useSidebar: Boolean): Dp =
|
||||
|
||||
@ -22,7 +22,6 @@ import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -35,11 +34,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.composeunstyled.Text
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.krtirtho.spotube.NavigationItem
|
||||
import dev.krtirtho.spotube.core.navigation.NavigationState
|
||||
import dev.krtirtho.spotube.core.navigation.Navigator
|
||||
@ -67,7 +66,6 @@ import spotube.composeapp.generated.resources.cookie_regular
|
||||
|
||||
@Composable
|
||||
fun AppSidebar(
|
||||
hazeState: HazeState,
|
||||
navigator: Navigator,
|
||||
navigationState: NavigationState,
|
||||
modifier: Modifier = Modifier,
|
||||
@ -80,6 +78,7 @@ fun AppSidebar(
|
||||
MillenniumSidebar(
|
||||
modifier = modifier,
|
||||
collapsed = !expanded,
|
||||
shape = RectangleShape,
|
||||
onCollapsedChange = { expanded = !it },
|
||||
) {
|
||||
MillenniumSidebarHeader {
|
||||
|
||||
@ -17,7 +17,11 @@
|
||||
|
||||
package dev.krtirtho.spotube.modules.shell.alternative_track
|
||||
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -32,8 +36,8 @@ fun AlternativeTrackSheet(
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit) {
|
||||
BoxWithConstraints(modifier = modifier) {
|
||||
if (maxWidth >= SlidingSheetBreakpoint) {
|
||||
Box(modifier = modifier) {
|
||||
if (currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Expanded) {
|
||||
EndDrawerSheet(isVisible, onDismiss, content)
|
||||
} else {
|
||||
BottomSheetContent(isVisible, onDismiss, content)
|
||||
|
||||
@ -18,18 +18,8 @@
|
||||
package dev.krtirtho.spotube.modules.shell.player_queue
|
||||
|
||||
|
||||
import org.spotube.millennium.composables.MillenniumIcon
|
||||
import org.spotube.millennium.composables.MillenniumCard
|
||||
import org.spotube.millennium.composables.MillenniumDropdownMenu
|
||||
import org.spotube.millennium.composables.MillenniumIconButton
|
||||
import org.spotube.millennium.composables.MillenniumIconButtonStyle
|
||||
import org.spotube.millennium.composables.MillenniumListTile
|
||||
import org.spotube.millennium.composables.MillenniumSeparator
|
||||
import org.spotube.millennium.composables.MillenniumTextField
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -45,14 +35,10 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
|
||||
import com.composeunstyled.Text
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@ -60,29 +46,47 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import com.composeunstyled.Text
|
||||
import dev.krtirtho.spotube.core.jam.JamParticipant
|
||||
import dev.krtirtho.spotube.core.ui.component.AdaptiveDialogBottomSheet
|
||||
import dev.krtirtho.spotube.core.ui.theming.AppTheme
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax3DotsMore
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxDragHandle
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxFilterSearch
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxCloseSquare
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxDragHandle
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxMusicSquareRemove
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxTrash
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxUserRemove
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.spotube.millennium.composables.MillenniumDropdownMenu
|
||||
import org.spotube.millennium.composables.MillenniumIcon
|
||||
import org.spotube.millennium.composables.MillenniumIconButton
|
||||
import org.spotube.millennium.composables.MillenniumIconButtonStyle
|
||||
import org.spotube.millennium.composables.MillenniumListTile
|
||||
import org.spotube.millennium.composables.MillenniumSeparator
|
||||
import org.spotube.millennium.composables.MillenniumTextField
|
||||
import org.spotube.millennium.composables.MillenniumTooltip
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
import spotube.composeapp.generated.resources.Res
|
||||
import spotube.composeapp.generated.resources.queue_clear_queue
|
||||
import spotube.composeapp.generated.resources.queue_filter_queue
|
||||
import spotube.composeapp.generated.resources.queue_no_queue_entries
|
||||
import spotube.composeapp.generated.resources.queue_title
|
||||
|
||||
@Composable
|
||||
fun PlayerQueueContent(
|
||||
viewModel: PlayerQueueContentViewModel = koinViewModel<PlayerQueueContentViewModel>(),
|
||||
modifier: Modifier = Modifier) {
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val state by viewModel.queueContentUiState.collectAsState()
|
||||
val displayItems = state.displayItems
|
||||
val filterQuery = state.filterQuery
|
||||
@ -103,14 +107,17 @@ fun PlayerQueueContent(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Queue",
|
||||
style = AppTheme.typography.titleLarge)
|
||||
text = stringResource(Res.string.queue_title),
|
||||
style = AppTheme.typography.titleLarge
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically) {
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val filterState = rememberTextFieldState(filterQuery)
|
||||
LaunchedEffect(filterState.text) {
|
||||
viewModel.setQueueFilter(filterState.text.toString())
|
||||
@ -118,42 +125,49 @@ fun PlayerQueueContent(
|
||||
MillenniumTextField(
|
||||
state = filterState,
|
||||
label = "",
|
||||
placeholder = "Filter queue...",
|
||||
modifier = Modifier.weight(1f))
|
||||
placeholder = stringResource(Res.string.queue_filter_queue),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
if (!isReadOnly) {
|
||||
MillenniumIconButton(
|
||||
onClick = viewModel::clearQueue,
|
||||
style = MillenniumIconButtonStyle.Quiet) {
|
||||
MillenniumIcon(Iconsax.IconsaxTrash, contentDescription = "Clear Queue")
|
||||
MillenniumTooltip(stringResource(Res.string.queue_clear_queue)) {
|
||||
MillenniumIconButton(
|
||||
onClick = viewModel::clearQueue,
|
||||
style = MillenniumIconButtonStyle.Quiet
|
||||
) {
|
||||
MillenniumIcon(
|
||||
Iconsax.IconsaxTrash,
|
||||
contentDescription = stringResource(Res.string.queue_clear_queue)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayItems.isEmpty()) {
|
||||
Text(
|
||||
text = "No queue entries",
|
||||
text = stringResource(Res.string.queue_no_queue_entries),
|
||||
style = AppTheme.typography.bodyMedium,
|
||||
color = AppTheme.colors.onSurfaceVariant)
|
||||
color = AppTheme.colors.onSurfaceVariant
|
||||
)
|
||||
} else {
|
||||
MillenniumCard {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(bottom = 8.dp)) {
|
||||
items(displayItems, key = { it.id }) { item ->
|
||||
ReorderableItem(reorderableLazyListState, key = item.id) { isDragging ->
|
||||
val elevation by animateDpAsState(if (isDragging) 8.dp else 0.dp)
|
||||
QueueItemRow(
|
||||
item = item,
|
||||
reorderScope = if (isFiltered || isReadOnly) null else this,
|
||||
onPlayClick = { viewModel.playQueueItem(item.originalIndex) },
|
||||
onRemoveClick = { viewModel.removeQueueItem(item.originalIndex) },
|
||||
onDragStarted = { viewModel.onDragStart() },
|
||||
onDragStopped = { viewModel.onDragStop() },
|
||||
showOptions = !isReadOnly,
|
||||
enabled = !isReadOnly,
|
||||
onParticipantClick = { selectedParticipant = it })
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(bottom = 8.dp)
|
||||
) {
|
||||
items(displayItems, key = { it.id }) { item ->
|
||||
ReorderableItem(reorderableLazyListState, key = item.id) { isDragging ->
|
||||
QueueItemRow(
|
||||
item = item,
|
||||
reorderScope = if (isFiltered || isReadOnly) null else this,
|
||||
onPlayClick = { viewModel.playQueueItem(item.originalIndex) },
|
||||
onRemoveClick = { viewModel.removeQueueItem(item.originalIndex) },
|
||||
onDragStarted = { viewModel.onDragStart() },
|
||||
onDragStopped = { viewModel.onDragStop() },
|
||||
showOptions = !isReadOnly,
|
||||
enabled = !isReadOnly,
|
||||
onParticipantClick = { selectedParticipant = it })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,32 +193,38 @@ private fun ParticipantDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onKick: () -> Unit,
|
||||
onBan: () -> Unit,
|
||||
onRemoveSuggestions: () -> Unit) {
|
||||
onRemoveSuggestions: () -> Unit
|
||||
) {
|
||||
AdaptiveDialogBottomSheet(
|
||||
onDismiss = onDismiss,
|
||||
title = { Text(participant.displayName, style = AppTheme.typography.titleLarge) }) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier.padding(vertical = 8.dp)) {
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
) {
|
||||
ParticipantAvatar(participant, size = 40)
|
||||
Text(
|
||||
text = participant.displayName,
|
||||
style = AppTheme.typography.bodyLarge)
|
||||
style = AppTheme.typography.bodyLarge
|
||||
)
|
||||
if (participant.isHost) {
|
||||
Text(
|
||||
text = "Host",
|
||||
style = AppTheme.typography.labelSmall,
|
||||
color = AppTheme.colors.primary)
|
||||
color = AppTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (isJamHost && !participant.isHost) {
|
||||
MillenniumSeparator(
|
||||
modifier = Modifier.fillMaxWidth())
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
MillenniumListTile(
|
||||
onClick = {
|
||||
onKick()
|
||||
@ -214,7 +234,8 @@ private fun ParticipantDialog(
|
||||
MillenniumIcon(
|
||||
imageVector = Iconsax.IconsaxCloseSquare,
|
||||
contentDescription = null,
|
||||
tint = AppTheme.colors.error)
|
||||
tint = AppTheme.colors.error
|
||||
)
|
||||
},
|
||||
title = { Text("Kick") },
|
||||
subtitle = { Text("Remove them from the session") })
|
||||
@ -227,7 +248,8 @@ private fun ParticipantDialog(
|
||||
MillenniumIcon(
|
||||
imageVector = Iconsax.IconsaxUserRemove,
|
||||
contentDescription = null,
|
||||
tint = AppTheme.colors.error)
|
||||
tint = AppTheme.colors.error
|
||||
)
|
||||
},
|
||||
title = { Text("Ban") },
|
||||
subtitle = { Text("Kick and prevent them from rejoining") })
|
||||
@ -239,7 +261,8 @@ private fun ParticipantDialog(
|
||||
leading = {
|
||||
MillenniumIcon(
|
||||
imageVector = Iconsax.IconsaxMusicSquareRemove,
|
||||
contentDescription = null)
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
title = { Text("Remove suggestions") },
|
||||
subtitle = { Text("Remove every track they added to the queue") })
|
||||
@ -255,11 +278,13 @@ private fun ParticipantAvatar(participant: JamParticipant, size: Int) {
|
||||
.size(size.dp)
|
||||
.clip(CircleShape)
|
||||
.background(AppTheme.colors.primaryContainer),
|
||||
contentAlignment = Alignment.Center) {
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = participant.displayName.firstOrNull()?.uppercase()?.take(1) ?: "?",
|
||||
style = AppTheme.typography.labelMedium,
|
||||
color = AppTheme.colors.onPrimaryContainer)
|
||||
color = AppTheme.colors.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +298,8 @@ private fun QueueItemRow(
|
||||
onDragStopped: () -> Unit,
|
||||
showOptions: Boolean = true,
|
||||
enabled: Boolean = true,
|
||||
onParticipantClick: (JamParticipant) -> Unit = {}) {
|
||||
onParticipantClick: (JamParticipant) -> Unit = {}
|
||||
) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
|
||||
MillenniumListTile(
|
||||
@ -284,8 +310,9 @@ private fun QueueItemRow(
|
||||
leading = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(72.dp),
|
||||
verticalAlignment = Alignment.CenterVertically) {
|
||||
.height(56.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
MillenniumIcon(
|
||||
Iconsax.IconsaxDragHandle,
|
||||
contentDescription = if (reorderScope != null) "Reorder" else null,
|
||||
@ -296,7 +323,8 @@ private fun QueueItemRow(
|
||||
with(reorderScope) {
|
||||
Modifier.draggableHandle(
|
||||
onDragStarted = { onDragStarted() },
|
||||
onDragStopped = onDragStopped)
|
||||
onDragStopped = onDragStopped
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
@ -305,21 +333,24 @@ private fun QueueItemRow(
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.size(40.dp)
|
||||
.clip(AppTheme.shapes.small)
|
||||
.background(AppTheme.colors.surfaceVariant),
|
||||
contentAlignment = Alignment.Center) {
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (item.imageUrl != null) {
|
||||
AsyncImage(
|
||||
model = item.imageUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop)
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "${item.originalIndex + 1}",
|
||||
style = AppTheme.typography.labelMedium,
|
||||
color = AppTheme.colors.onSurfaceVariant)
|
||||
color = AppTheme.colors.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -334,7 +365,8 @@ private fun QueueItemRow(
|
||||
AppTheme.colors.onSecondaryContainer
|
||||
} else {
|
||||
AppTheme.colors.onSurface
|
||||
})
|
||||
}
|
||||
)
|
||||
},
|
||||
subtitle = {
|
||||
Text(
|
||||
@ -342,13 +374,15 @@ private fun QueueItemRow(
|
||||
style = AppTheme.typography.bodySmall,
|
||||
color = AppTheme.colors.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis)
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
Text(
|
||||
text = item.durationLabel,
|
||||
style = AppTheme.typography.bodySmall,
|
||||
color = AppTheme.colors.onSurfaceVariant)
|
||||
color = AppTheme.colors.onSurfaceVariant
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
|
||||
@ -359,11 +393,13 @@ private fun QueueItemRow(
|
||||
.clip(CircleShape)
|
||||
.background(AppTheme.colors.surfaceVariant)
|
||||
.clickable { onParticipantClick(participant) },
|
||||
contentAlignment = Alignment.Center) {
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = participant.displayName.firstOrNull()?.uppercase()?.take(1) ?: "?",
|
||||
style = AppTheme.typography.labelSmall,
|
||||
color = AppTheme.colors.onSurfaceVariant)
|
||||
color = AppTheme.colors.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
@ -380,10 +416,12 @@ private fun QueueItemRow(
|
||||
anchor = {
|
||||
MillenniumIconButton(
|
||||
onClick = { showMenu = true },
|
||||
style = MillenniumIconButtonStyle.Quiet) {
|
||||
style = MillenniumIconButtonStyle.Quiet
|
||||
) {
|
||||
MillenniumIcon(
|
||||
Iconsax.Iconsax3DotsMore,
|
||||
contentDescription = "More options")
|
||||
contentDescription = "More options"
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -17,15 +17,17 @@
|
||||
|
||||
package dev.krtirtho.spotube.modules.shell.player_queue
|
||||
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.composeunstyled.DrawerPlacement
|
||||
import com.composeunstyled.SheetDetent
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import com.composeunstyled.rememberModalBottomSheetState
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import org.spotube.millennium.composables.MillenniumDrawer
|
||||
import org.spotube.millennium.composables.MillenniumDrawerValue
|
||||
import org.spotube.millennium.composables.MillenniumModalBottomSheet
|
||||
@ -38,13 +40,14 @@ fun QueueSheet(
|
||||
isVisible: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit) {
|
||||
BoxWithConstraints(modifier = modifier) {
|
||||
if (maxWidth >= SlidingSheetBreakpoint) {
|
||||
EndDrawerSheet(isVisible, onDismiss, content)
|
||||
} else {
|
||||
BottomSheetContent(isVisible, onDismiss, content)
|
||||
}
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val isExpanded = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Expanded
|
||||
|
||||
if (isExpanded) {
|
||||
EndDrawerSheet(isVisible, onDismiss, content)
|
||||
} else {
|
||||
BottomSheetContent(isVisible, onDismiss, content)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +55,8 @@ fun QueueSheet(
|
||||
internal fun EndDrawerSheet(
|
||||
isVisible: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
content: @Composable () -> Unit) {
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val drawerState = rememberMillenniumDrawerState()
|
||||
LaunchedEffect(isVisible) {
|
||||
drawerState.targetValue =
|
||||
@ -66,7 +70,10 @@ internal fun EndDrawerSheet(
|
||||
MillenniumDrawer(
|
||||
state = drawerState,
|
||||
placement = DrawerPlacement.End,
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 12.dp)) {
|
||||
shape = RectangleShape,
|
||||
maximumWidth = 400.dp,
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 12.dp)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
@ -75,7 +82,8 @@ internal fun EndDrawerSheet(
|
||||
internal fun BottomSheetContent(
|
||||
isVisible: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
content: @Composable () -> Unit) {
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
if (!isVisible) return
|
||||
val sheetState = rememberModalBottomSheetState(initialDetent = SheetDetent.FullyExpanded)
|
||||
LaunchedEffect(sheetState.bottomSheetState.currentDetent) {
|
||||
@ -85,7 +93,8 @@ internal fun BottomSheetContent(
|
||||
}
|
||||
MillenniumModalBottomSheet(
|
||||
state = sheetState,
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp)) {
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
package dev.krtirtho.spotube.modules.welcome.pages
|
||||
|
||||
|
||||
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||
import org.spotube.millennium.composables.MillenniumCard
|
||||
import org.spotube.millennium.composables.MillenniumIcon
|
||||
import org.spotube.millennium.composables.MillenniumIconButton
|
||||
@ -38,8 +43,8 @@ fun HowItWorksPage(
|
||||
onTogglePlayback: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
BoxWithConstraints(modifier.fillMaxSize()) {
|
||||
val wide = maxWidth >= 760.dp
|
||||
Box(modifier.fillMaxSize()) {
|
||||
val wide = currentWindowWidthBreakpoint() isAtLeast WindowBreakpoints.Expanded
|
||||
if (wide) {
|
||||
Row(
|
||||
Modifier.fillMaxSize().padding(40.dp),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user