mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-11-04 10:13:59 +00:00
feat(navigation): refactor sidebar and bottombar navigation structure with new tab and group models
This commit is contained in:
parent
78168d269c
commit
86ec117cfd
@ -40,56 +40,91 @@ import dev.krtirtho.spotube.core.navigation.rememberNavigationState
|
|||||||
import dev.krtirtho.spotube.core.navigation.toEntries
|
import dev.krtirtho.spotube.core.navigation.toEntries
|
||||||
import dev.krtirtho.spotube.core.ui.component.LocalSharedTransitionScope
|
import dev.krtirtho.spotube.core.ui.component.LocalSharedTransitionScope
|
||||||
import dev.krtirtho.spotube.core.ui.theming.SpotubeTheme
|
import dev.krtirtho.spotube.core.ui.theming.SpotubeTheme
|
||||||
|
import dev.krtirtho.spotube.modules.library.LibraryTab
|
||||||
import dev.krtirtho.spotube.modules.settings.SettingsRepository
|
import dev.krtirtho.spotube.modules.settings.SettingsRepository
|
||||||
import dev.krtirtho.spotube.modules.settings.UserSettings
|
import dev.krtirtho.spotube.modules.settings.UserSettings
|
||||||
import dev.krtirtho.spotube.modules.shell.AppShell
|
import dev.krtirtho.spotube.modules.shell.AppShell
|
||||||
import dev.krtirtho.spotube.modules.webview.WebViewScreen
|
import dev.krtirtho.spotube.modules.webview.WebViewScreen
|
||||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxCd
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxDirectboxReceive
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxHome
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxHome
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxHomeBroken
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxHomeBroken
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxMirrorScreenRegular
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxMirroringScreen
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxMusicDashboard
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxMusicLibrary
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxMusicLibrary
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxMusicLibraryOutline
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxMusicLibraryOutline
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxSearch
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSearch
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxSearchBroken
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSearchBroken
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxSetting2
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSetting2
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxSettingTwotone
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSettingTwotone
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSound
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSoundTwotone
|
||||||
|
import dev.krtirtho.spotube.resources.iconsax.User
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
import org.koin.compose.navigation3.koinEntryProvider
|
import org.koin.compose.navigation3.koinEntryProvider
|
||||||
import org.koin.core.annotation.KoinExperimentalAPI
|
import org.koin.core.annotation.KoinExperimentalAPI
|
||||||
|
|
||||||
data class TabItem(
|
interface NavigationItem {
|
||||||
val title: String,
|
data class Tab<T : Any>(
|
||||||
val icon: ImageVector,
|
val title: String, val icon: ImageVector, val route: Routes,
|
||||||
val activeIcon: ImageVector,
|
val data: T? = null
|
||||||
val route: Routes
|
) : NavigationItem
|
||||||
|
|
||||||
|
data class Group<T : Any>(
|
||||||
|
val title: String, val items: List<Tab<T>>
|
||||||
|
) : NavigationItem
|
||||||
|
}
|
||||||
|
|
||||||
|
private val tabs = listOf<NavigationItem>(
|
||||||
|
NavigationItem.Tab<Any>("Home", Iconsax.IconsaxHome, Routes.Home),
|
||||||
|
NavigationItem.Tab<Any>("Search", Iconsax.IconsaxSearch, Routes.Search),
|
||||||
)
|
)
|
||||||
|
|
||||||
val tabs = listOf(
|
val sidebarTabs = tabs + listOf(
|
||||||
TabItem(
|
NavigationItem.Group(
|
||||||
"Home",
|
|
||||||
Iconsax.IconsaxHomeBroken,
|
|
||||||
Iconsax.IconsaxHome,
|
|
||||||
Routes.Home
|
|
||||||
),
|
|
||||||
TabItem(
|
|
||||||
"Search",
|
|
||||||
Iconsax.IconsaxSearchBroken,
|
|
||||||
Iconsax.IconsaxSearch,
|
|
||||||
Routes.Search
|
|
||||||
),
|
|
||||||
TabItem(
|
|
||||||
"Library",
|
"Library",
|
||||||
Iconsax.IconsaxMusicLibraryOutline,
|
items = listOf(
|
||||||
Iconsax.IconsaxMusicLibrary,
|
NavigationItem.Tab(
|
||||||
Routes.Library
|
"Playlists",
|
||||||
|
Iconsax.IconsaxMusicDashboard,
|
||||||
|
Routes.Library,
|
||||||
|
LibraryTab.Playlists
|
||||||
),
|
),
|
||||||
TabItem(
|
NavigationItem.Tab("Albums", Iconsax.IconsaxCd, Routes.Library, LibraryTab.Albums),
|
||||||
"Settings",
|
NavigationItem.Tab("Artists", Iconsax.User, Routes.Library, LibraryTab.Artists),
|
||||||
Iconsax.IconsaxSettingTwotone,
|
|
||||||
Iconsax.IconsaxSetting2,
|
|
||||||
Routes.Settings
|
|
||||||
)
|
)
|
||||||
|
),
|
||||||
|
NavigationItem.Group(
|
||||||
|
"On Device",
|
||||||
|
items = listOf(
|
||||||
|
NavigationItem.Tab(
|
||||||
|
"Local Tracks",
|
||||||
|
Iconsax.IconsaxMusicLibrary,
|
||||||
|
Routes.Library,
|
||||||
|
LibraryTab.LocalTracks
|
||||||
|
),
|
||||||
|
NavigationItem.Tab(
|
||||||
|
"Downloads",
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val bottombarTabs = tabs + listOf(
|
||||||
|
NavigationItem.Tab<Any>("Library", Iconsax.IconsaxMusicLibrary, Routes.Library),
|
||||||
|
NavigationItem.Tab("Settings", Iconsax.IconsaxSetting2, Routes.Settings)
|
||||||
)
|
)
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
@ -105,8 +140,7 @@ fun App(
|
|||||||
val userSettings by settingsRepository.userSettings.collectAsStateWithLifecycle(initialValue = UserSettings())
|
val userSettings by settingsRepository.userSettings.collectAsStateWithLifecycle(initialValue = UserSettings())
|
||||||
|
|
||||||
val navigationState = rememberNavigationState(
|
val navigationState = rememberNavigationState(
|
||||||
startRoute = Routes.Home,
|
startRoute = Routes.Home, topLevelRoutes = TOP_LEVEL_ROUTES
|
||||||
topLevelRoutes = TOP_LEVEL_ROUTES
|
|
||||||
)
|
)
|
||||||
val navigator = remember {
|
val navigator = remember {
|
||||||
Navigator(navigationState)
|
Navigator(navigationState)
|
||||||
|
|||||||
@ -38,10 +38,16 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TooltipAnchorPosition
|
||||||
|
import androidx.compose.material3.TooltipBox
|
||||||
|
import androidx.compose.material3.TooltipDefaults
|
||||||
|
import androidx.compose.material3.rememberTooltipState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -81,6 +87,7 @@ import dev.krtirtho.spotube.core.ui.component.cards.PlayableCard
|
|||||||
import dev.krtirtho.spotube.core.ui.component.dragScrollable
|
import dev.krtirtho.spotube.core.ui.component.dragScrollable
|
||||||
import dev.krtirtho.spotube.core.ui.misc.SkeletonTree
|
import dev.krtirtho.spotube.core.ui.misc.SkeletonTree
|
||||||
import dev.krtirtho.spotube.modules.library.playlist.AddToPlaylistPicker
|
import dev.krtirtho.spotube.modules.library.playlist.AddToPlaylistPicker
|
||||||
|
import dev.krtirtho.spotube.modules.shell.LocalAppShellBottomInset
|
||||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxAddSquare
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxAddSquare
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxPlay
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxPlay
|
||||||
@ -134,7 +141,8 @@ fun ArtistScreen(
|
|||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(innerPadding),
|
.padding(innerPadding)
|
||||||
|
.padding(bottom = LocalAppShellBottomInset.current + 24.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
@ -495,6 +503,7 @@ private fun ArtistMeta(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ArtistHeaderActions(
|
private fun ArtistHeaderActions(
|
||||||
isSaved: Boolean,
|
isSaved: Boolean,
|
||||||
@ -508,7 +517,12 @@ private fun ArtistHeaderActions(
|
|||||||
) {
|
) {
|
||||||
if (isSaved) {
|
if (isSaved) {
|
||||||
SecondaryButton(onClick = onFollowClick) {
|
SecondaryButton(onClick = onFollowClick) {
|
||||||
Text("Following", modifier = Modifier.width(65.dp), textAlign = TextAlign.Center)
|
Text(
|
||||||
|
"Following",
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PrimaryButton(onClick = onFollowClick) {
|
PrimaryButton(onClick = onFollowClick) {
|
||||||
@ -516,6 +530,15 @@ private fun ArtistHeaderActions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TooltipBox(
|
||||||
|
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
|
||||||
|
tooltip = {
|
||||||
|
PlainTooltip {
|
||||||
|
Text(if (isBlacklisted) "Remove from blacklist" else "Add to blacklist")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state = rememberTooltipState()
|
||||||
|
) {
|
||||||
if (isBlacklisted) {
|
if (isBlacklisted) {
|
||||||
SecondaryIconButton(onClick = onBlacklistClick) {
|
SecondaryIconButton(onClick = onBlacklistClick) {
|
||||||
Icon(
|
Icon(
|
||||||
@ -534,6 +557,7 @@ private fun ArtistHeaderActions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopTracksHeader(
|
private fun TopTracksHeader(
|
||||||
|
|||||||
@ -54,6 +54,8 @@ import androidx.compose.ui.unit.sp
|
|||||||
import compose.icons.FeatherIcons
|
import compose.icons.FeatherIcons
|
||||||
import compose.icons.feathericons.X
|
import compose.icons.feathericons.X
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.lyrics.LyricsLine
|
import dev.krtirtho.plugin_interfaces.plugin_apis.lyrics.LyricsLine
|
||||||
|
import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar
|
||||||
|
import dev.krtirtho.spotube.modules.shell.LocalAppShellBottomInset
|
||||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxArrowDown4
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxArrowDown4
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -70,18 +72,12 @@ fun LyricsScreen(
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
topBar = {
|
||||||
) { innerPadding ->
|
ApplicationMainBar(
|
||||||
Column(
|
backButton = onClose == null,
|
||||||
modifier = Modifier
|
title = {
|
||||||
.fillMaxSize()
|
|
||||||
.padding(innerPadding)
|
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
) {
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
|
||||||
) {
|
) {
|
||||||
if (onClose != null) {
|
if (onClose != null) {
|
||||||
IconButton(
|
IconButton(
|
||||||
@ -114,9 +110,19 @@ fun LyricsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(innerPadding)
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = LocalAppShellBottomInset.current + 24.dp)
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
when {
|
when {
|
||||||
uiState.isLoading -> {
|
uiState.isLoading -> {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@ -42,11 +42,12 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import dev.krtirtho.spotube.NavigationItem
|
||||||
|
import dev.krtirtho.spotube.bottombarTabs
|
||||||
import dev.krtirtho.spotube.core.navigation.NavigationState
|
import dev.krtirtho.spotube.core.navigation.NavigationState
|
||||||
import dev.krtirtho.spotube.core.navigation.Navigator
|
import dev.krtirtho.spotube.core.navigation.Navigator
|
||||||
import dev.krtirtho.spotube.core.navigation.Routes
|
import dev.krtirtho.spotube.core.navigation.Routes
|
||||||
import dev.krtirtho.spotube.modules.downloads.DownloadBadgeIndicator
|
import dev.krtirtho.spotube.modules.downloads.DownloadBadgeIndicator
|
||||||
import dev.krtirtho.spotube.tabs
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppBottombar(
|
fun AppBottombar(
|
||||||
@ -68,8 +69,9 @@ fun AppBottombar(
|
|||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
tabs.forEach { (label, icon, activeIcon, screen) ->
|
bottombarTabs.filterIsInstance<NavigationItem.Tab<*>>()
|
||||||
val selected = navigationState.topLevelRoute == screen
|
.forEach { tab ->
|
||||||
|
val selected = navigationState.topLevelRoute == tab.route
|
||||||
val labelColor = if (selected) {
|
val labelColor = if (selected) {
|
||||||
MaterialTheme.colorScheme.primary
|
MaterialTheme.colorScheme.primary
|
||||||
} else {
|
} else {
|
||||||
@ -79,7 +81,7 @@ fun AppBottombar(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.clickable { navigator.navigate(screen) }
|
.clickable { navigator.navigate(tab.route) }
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.size(56.dp),
|
.size(56.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
@ -87,13 +89,13 @@ fun AppBottombar(
|
|||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = activeIcon,
|
imageVector = tab.icon,
|
||||||
contentDescription = label,
|
contentDescription = tab.title,
|
||||||
tint = if (selected) MaterialTheme.colorScheme.primary
|
tint = if (selected) MaterialTheme.colorScheme.primary
|
||||||
else MaterialTheme.colorScheme.onSurfaceVariant,
|
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier = Modifier.size(20.dp)
|
modifier = Modifier.size(20.dp)
|
||||||
)
|
)
|
||||||
if (screen == Routes.Library) {
|
if (tab.route == Routes.Library) {
|
||||||
DownloadBadgeIndicator(
|
DownloadBadgeIndicator(
|
||||||
modifier = Modifier.align(Alignment.TopEnd)
|
modifier = Modifier.align(Alignment.TopEnd)
|
||||||
)
|
)
|
||||||
@ -101,7 +103,7 @@ fun AppBottombar(
|
|||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
Text(
|
Text(
|
||||||
text = label,
|
text = tab.title,
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
fontWeight = if (selected) FontWeight.Medium else FontWeight.Normal,
|
fontWeight = if (selected) FontWeight.Medium else FontWeight.Normal,
|
||||||
color = labelColor,
|
color = labelColor,
|
||||||
|
|||||||
@ -29,12 +29,16 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@ -55,6 +59,7 @@ import dev.chrisbanes.haze.HazeState
|
|||||||
import dev.chrisbanes.haze.blur.HazeColorEffect
|
import dev.chrisbanes.haze.blur.HazeColorEffect
|
||||||
import dev.chrisbanes.haze.blur.blurEffect
|
import dev.chrisbanes.haze.blur.blurEffect
|
||||||
import dev.chrisbanes.haze.hazeEffect
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
|
import dev.krtirtho.spotube.NavigationItem
|
||||||
import dev.krtirtho.spotube.core.navigation.NavigationState
|
import dev.krtirtho.spotube.core.navigation.NavigationState
|
||||||
import dev.krtirtho.spotube.core.navigation.Navigator
|
import dev.krtirtho.spotube.core.navigation.Navigator
|
||||||
import dev.krtirtho.spotube.core.navigation.Routes
|
import dev.krtirtho.spotube.core.navigation.Routes
|
||||||
@ -64,15 +69,15 @@ import dev.krtirtho.spotube.core.ui.base.OutlineButton
|
|||||||
import dev.krtirtho.spotube.core.ui.base.SecondaryButton
|
import dev.krtirtho.spotube.core.ui.base.SecondaryButton
|
||||||
import dev.krtirtho.spotube.core.ui.base.copyPadding
|
import dev.krtirtho.spotube.core.ui.base.copyPadding
|
||||||
import dev.krtirtho.spotube.core.ui.base.copyShape
|
import dev.krtirtho.spotube.core.ui.base.copyShape
|
||||||
|
import dev.krtirtho.spotube.core.ui.component.VerticalScrollbar
|
||||||
import dev.krtirtho.spotube.modules.downloads.DownloadBadgeIndicator
|
import dev.krtirtho.spotube.modules.downloads.DownloadBadgeIndicator
|
||||||
import dev.krtirtho.spotube.modules.library.LibraryState
|
import dev.krtirtho.spotube.modules.library.LibraryState
|
||||||
import dev.krtirtho.spotube.modules.library.LibraryTab
|
import dev.krtirtho.spotube.modules.library.LibraryTab
|
||||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxMirroringScreen
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSetting2
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxSidebarLeftBroken
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSidebarLeftBroken
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxSidebarRightBroken
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxSidebarRightBroken
|
||||||
import dev.krtirtho.spotube.resources.iconsax.User
|
import dev.krtirtho.spotube.sidebarTabs
|
||||||
import dev.krtirtho.spotube.tabs
|
|
||||||
import org.jetbrains.compose.resources.Font
|
import org.jetbrains.compose.resources.Font
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
import spotube.composeapp.generated.resources.Res
|
import spotube.composeapp.generated.resources.Res
|
||||||
@ -90,6 +95,7 @@ fun AppSidebar(
|
|||||||
val width by animateDpAsState(targetValue = if (expanded) 236.dp else 86.dp)
|
val width by animateDpAsState(targetValue = if (expanded) 236.dp else 86.dp)
|
||||||
val currentLibraryTab by libraryState.currentTab.collectAsState()
|
val currentLibraryTab by libraryState.currentTab.collectAsState()
|
||||||
val surfaceTint = MaterialTheme.colorScheme.surface.copy(alpha = 0.85f)
|
val surfaceTint = MaterialTheme.colorScheme.surface.copy(alpha = 0.85f)
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@ -139,62 +145,74 @@ fun AppSidebar(
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
tabs.forEach { (label, icon, activeIcon, screen) ->
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
val selected = navigationState.topLevelRoute == screen
|
LazyColumn(state = listState) {
|
||||||
|
items(sidebarTabs.size) { index ->
|
||||||
if (screen == Routes.Library) {
|
when (val tab = sidebarTabs[index]) {
|
||||||
AnimatedVisibility(visible = expanded) {
|
is NavigationItem.Group<*> -> {
|
||||||
|
androidx.compose.animation.AnimatedVisibility(visible = expanded) {
|
||||||
Text(
|
Text(
|
||||||
text = "LIBRARY",
|
text = tab.title.uppercase(),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
.padding(horizontal = 24.dp, vertical = 8.dp)
|
.padding(horizontal = 24.dp, vertical = 8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
tab.items.forEach {
|
||||||
|
val selected = navigationState.topLevelRoute == it.route
|
||||||
|
SidebarItem(
|
||||||
|
label = it.title,
|
||||||
|
activeIcon = it.icon,
|
||||||
|
onClick = {
|
||||||
|
if (it.route == Routes.Library) {
|
||||||
|
libraryState.currentTab.value =
|
||||||
|
it.data as LibraryTab
|
||||||
|
}
|
||||||
|
navigator.navigate(it.route)
|
||||||
|
},
|
||||||
|
selected = if (it.route == Routes.Library) selected && currentLibraryTab == it.data else selected,
|
||||||
|
expanded = expanded,
|
||||||
|
showDownloadBadge = it.route == Routes.Library
|
||||||
|
&& it.data == LibraryTab.Downloads
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LibraryTab.entries.forEach { tab ->
|
}
|
||||||
val isSubSelected = selected && currentLibraryTab == tab
|
|
||||||
|
|
||||||
|
is NavigationItem.Tab<*> -> {
|
||||||
|
val selected = navigationState.topLevelRoute == tab.route
|
||||||
SidebarItem(
|
SidebarItem(
|
||||||
label = tab.title,
|
label = tab.title,
|
||||||
activeIcon = tab.icon,
|
activeIcon = tab.icon,
|
||||||
onClick = {
|
onClick = {
|
||||||
libraryState.currentTab.value = tab
|
navigator.navigate(tab.route)
|
||||||
navigator.navigate(screen)
|
|
||||||
},
|
},
|
||||||
selected = isSubSelected,
|
|
||||||
expanded = expanded,
|
|
||||||
showDownloadBadge = tab == LibraryTab.Downloads,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SidebarItem(
|
|
||||||
label = label,
|
|
||||||
activeIcon = activeIcon,
|
|
||||||
onClick = { navigator.navigate(screen) },
|
|
||||||
selected = selected,
|
selected = selected,
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
VerticalScrollbar(
|
||||||
|
listState = listState,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterEnd)
|
||||||
|
.fillMaxHeight()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalDivider()
|
||||||
SidebarItem(
|
SidebarItem(
|
||||||
label = "Devices",
|
label = "Settings",
|
||||||
activeIcon = Iconsax.IconsaxMirroringScreen,
|
activeIcon = Iconsax.IconsaxSetting2,
|
||||||
onClick = { navigator.navigate(Routes.Devices) },
|
onClick = {
|
||||||
selected = false,
|
navigator.navigate(Routes.Settings)
|
||||||
|
},
|
||||||
|
selected = navigationState.topLevelRoute == Routes.Settings,
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
)
|
)
|
||||||
SidebarItem(
|
|
||||||
label = "Group Jam",
|
|
||||||
activeIcon = Iconsax.User,
|
|
||||||
onClick = { navigator.navigate(Routes.Jam) },
|
|
||||||
selected = false,
|
|
||||||
expanded = expanded,
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(120.dp))
|
Spacer(modifier = Modifier.height(120.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,7 +227,7 @@ fun SidebarItem(
|
|||||||
showDownloadBadge: Boolean = false,
|
showDownloadBadge: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val itemContent: @Composable RowScope.() -> Unit = {
|
val itemContent: @Composable RowScope.() -> Unit = {
|
||||||
Box(modifier = Modifier.size(20.dp)) {
|
Box(modifier = Modifier.size(16.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = activeIcon,
|
imageVector = activeIcon,
|
||||||
contentDescription = label,
|
contentDescription = label,
|
||||||
@ -231,7 +249,7 @@ fun SidebarItem(
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
softWrap = false,
|
softWrap = false,
|
||||||
style = LocalTextStyle.current.copy(
|
style = LocalTextStyle.current.copy(
|
||||||
fontSize = MaterialTheme.typography.labelLarge.fontSize,
|
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||||
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Medium,
|
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Medium,
|
||||||
color = if (selected) {
|
color = if (selected) {
|
||||||
MaterialTheme.colorScheme.onSecondaryContainer
|
MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
@ -246,8 +264,8 @@ fun SidebarItem(
|
|||||||
|
|
||||||
val buttonModifier = Modifier
|
val buttonModifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 10.dp, vertical = 4.dp)
|
.padding(horizontal = 7.dp, vertical = 2.dp)
|
||||||
val contentPadding = PaddingValues(horizontal = 7.dp, vertical = 6.dp)
|
val contentPadding = PaddingValues(horizontal = 5.dp)
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
@ -255,7 +273,7 @@ fun SidebarItem(
|
|||||||
modifier = buttonModifier,
|
modifier = buttonModifier,
|
||||||
theme = LocalBaseUITheme.current.buttons.secondary
|
theme = LocalBaseUITheme.current.buttons.secondary
|
||||||
.copyPadding(contentPadding)
|
.copyPadding(contentPadding)
|
||||||
.copyShape(RoundedCornerShape(8.dp)),
|
.copyShape(RoundedCornerShape(5.dp)),
|
||||||
content = itemContent,
|
content = itemContent,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -264,7 +282,7 @@ fun SidebarItem(
|
|||||||
modifier = buttonModifier,
|
modifier = buttonModifier,
|
||||||
theme = LocalBaseUITheme.current.buttons.outline
|
theme = LocalBaseUITheme.current.buttons.outline
|
||||||
.copyPadding(contentPadding)
|
.copyPadding(contentPadding)
|
||||||
.copyShape(RoundedCornerShape(8.dp)),
|
.copyShape(RoundedCornerShape(5.dp)),
|
||||||
hoverOnly = true,
|
hoverOnly = true,
|
||||||
content = itemContent,
|
content = itemContent,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
package dev.krtirtho.spotube.resources.iconsax
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.graphics.StrokeJoin
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.graphics.vector.PathData
|
||||||
|
import androidx.compose.ui.graphics.vector.group
|
||||||
|
import androidx.compose.ui.graphics.vector.path
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
val Iconsax.IconsaxMirrorScreenRegular: ImageVector
|
||||||
|
get() {
|
||||||
|
if (_IconsaxMirrorScreenRegular != null) {
|
||||||
|
return _IconsaxMirrorScreenRegular!!
|
||||||
|
}
|
||||||
|
_IconsaxMirrorScreenRegular = ImageVector.Builder(
|
||||||
|
name = "IconsaxMirrorScreenRegular",
|
||||||
|
defaultWidth = 24.dp,
|
||||||
|
defaultHeight = 24.dp,
|
||||||
|
viewportWidth = 24f,
|
||||||
|
viewportHeight = 24f
|
||||||
|
).apply {
|
||||||
|
group(
|
||||||
|
clipPathData = PathData {
|
||||||
|
moveTo(0f, 0f)
|
||||||
|
horizontalLineToRelative(24f)
|
||||||
|
verticalLineToRelative(24f)
|
||||||
|
horizontalLineToRelative(-24f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
path(
|
||||||
|
stroke = SolidColor(Color.White),
|
||||||
|
strokeLineWidth = 1.5f,
|
||||||
|
strokeLineCap = StrokeCap.Round,
|
||||||
|
strokeLineJoin = StrokeJoin.Round
|
||||||
|
) {
|
||||||
|
moveTo(2f, 9f)
|
||||||
|
verticalLineTo(8f)
|
||||||
|
curveTo(2f, 5f, 4f, 3f, 7f, 3f)
|
||||||
|
horizontalLineTo(17f)
|
||||||
|
curveTo(20f, 3f, 22f, 5f, 22f, 8f)
|
||||||
|
verticalLineTo(16f)
|
||||||
|
curveTo(22f, 19f, 20f, 21f, 17f, 21f)
|
||||||
|
horizontalLineTo(16f)
|
||||||
|
}
|
||||||
|
path(
|
||||||
|
fillAlpha = 0.4f,
|
||||||
|
stroke = SolidColor(Color.White),
|
||||||
|
strokeAlpha = 0.4f,
|
||||||
|
strokeLineWidth = 1.5f,
|
||||||
|
strokeLineCap = StrokeCap.Round,
|
||||||
|
strokeLineJoin = StrokeJoin.Round
|
||||||
|
) {
|
||||||
|
moveTo(3.69f, 11.711f)
|
||||||
|
curveTo(8.31f, 12.301f, 11.7f, 15.701f, 12.3f, 20.321f)
|
||||||
|
}
|
||||||
|
path(
|
||||||
|
fillAlpha = 0.4f,
|
||||||
|
stroke = SolidColor(Color.White),
|
||||||
|
strokeAlpha = 0.4f,
|
||||||
|
strokeLineWidth = 1.5f,
|
||||||
|
strokeLineCap = StrokeCap.Round,
|
||||||
|
strokeLineJoin = StrokeJoin.Round
|
||||||
|
) {
|
||||||
|
moveTo(2.62f, 15.07f)
|
||||||
|
curveTo(6.01f, 15.5f, 8.5f, 18f, 8.94f, 21.39f)
|
||||||
|
}
|
||||||
|
path(
|
||||||
|
fillAlpha = 0.4f,
|
||||||
|
stroke = SolidColor(Color.White),
|
||||||
|
strokeAlpha = 0.4f,
|
||||||
|
strokeLineWidth = 1.5f,
|
||||||
|
strokeLineCap = StrokeCap.Round,
|
||||||
|
strokeLineJoin = StrokeJoin.Round
|
||||||
|
) {
|
||||||
|
moveTo(1.98f, 18.859f)
|
||||||
|
curveTo(3.67f, 19.079f, 4.92f, 20.319f, 5.14f, 22.019f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return _IconsaxMirrorScreenRegular!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ObjectPropertyName")
|
||||||
|
private var _IconsaxMirrorScreenRegular: ImageVector? = null
|
||||||
@ -1,20 +1,3 @@
|
|||||||
/*
|
|
||||||
* 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.resources.iconsax
|
package dev.krtirtho.spotube.resources.iconsax
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@ -56,11 +39,7 @@ val Iconsax.IconsaxSound: ImageVector
|
|||||||
curveTo(3.75f, 16.16f, 3.41f, 16.5f, 3f, 16.5f)
|
curveTo(3.75f, 16.16f, 3.41f, 16.5f, 3f, 16.5f)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
path(
|
path(fill = SolidColor(Color.White)) {
|
||||||
fill = SolidColor(Color.White),
|
|
||||||
fillAlpha = 0.4f,
|
|
||||||
strokeAlpha = 0.4f
|
|
||||||
) {
|
|
||||||
moveTo(7.5f, 19f)
|
moveTo(7.5f, 19f)
|
||||||
curveTo(7.09f, 19f, 6.75f, 18.66f, 6.75f, 18.25f)
|
curveTo(7.09f, 19f, 6.75f, 18.66f, 6.75f, 18.25f)
|
||||||
verticalLineTo(5.75f)
|
verticalLineTo(5.75f)
|
||||||
@ -80,11 +59,7 @@ val Iconsax.IconsaxSound: ImageVector
|
|||||||
curveTo(12.75f, 21.16f, 12.41f, 21.5f, 12f, 21.5f)
|
curveTo(12.75f, 21.16f, 12.41f, 21.5f, 12f, 21.5f)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
path(
|
path(fill = SolidColor(Color.White)) {
|
||||||
fill = SolidColor(Color.White),
|
|
||||||
fillAlpha = 0.4f,
|
|
||||||
strokeAlpha = 0.4f
|
|
||||||
) {
|
|
||||||
moveTo(16.5f, 19f)
|
moveTo(16.5f, 19f)
|
||||||
curveTo(16.09f, 19f, 15.75f, 18.66f, 15.75f, 18.25f)
|
curveTo(16.09f, 19f, 15.75f, 18.66f, 15.75f, 18.25f)
|
||||||
verticalLineTo(5.75f)
|
verticalLineTo(5.75f)
|
||||||
|
|||||||
@ -0,0 +1,97 @@
|
|||||||
|
package dev.krtirtho.spotube.resources.iconsax
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.graphics.vector.PathData
|
||||||
|
import androidx.compose.ui.graphics.vector.group
|
||||||
|
import androidx.compose.ui.graphics.vector.path
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
val Iconsax.IconsaxSoundTwotone: ImageVector
|
||||||
|
get() {
|
||||||
|
if (_IconsaxSoundTwotone != null) {
|
||||||
|
return _IconsaxSoundTwotone!!
|
||||||
|
}
|
||||||
|
_IconsaxSoundTwotone = ImageVector.Builder(
|
||||||
|
name = "IconsaxSoundTwotone",
|
||||||
|
defaultWidth = 24.dp,
|
||||||
|
defaultHeight = 24.dp,
|
||||||
|
viewportWidth = 24f,
|
||||||
|
viewportHeight = 24f
|
||||||
|
).apply {
|
||||||
|
group(
|
||||||
|
clipPathData = PathData {
|
||||||
|
moveTo(0f, 0f)
|
||||||
|
horizontalLineToRelative(24f)
|
||||||
|
verticalLineToRelative(24f)
|
||||||
|
horizontalLineToRelative(-24f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
path(fill = SolidColor(Color.White)) {
|
||||||
|
moveTo(3f, 16.5f)
|
||||||
|
curveTo(2.59f, 16.5f, 2.25f, 16.16f, 2.25f, 15.75f)
|
||||||
|
verticalLineTo(8.25f)
|
||||||
|
curveTo(2.25f, 7.84f, 2.59f, 7.5f, 3f, 7.5f)
|
||||||
|
curveTo(3.41f, 7.5f, 3.75f, 7.84f, 3.75f, 8.25f)
|
||||||
|
verticalLineTo(15.75f)
|
||||||
|
curveTo(3.75f, 16.16f, 3.41f, 16.5f, 3f, 16.5f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
path(
|
||||||
|
fill = SolidColor(Color.White),
|
||||||
|
fillAlpha = 0.4f,
|
||||||
|
strokeAlpha = 0.4f
|
||||||
|
) {
|
||||||
|
moveTo(7.5f, 19f)
|
||||||
|
curveTo(7.09f, 19f, 6.75f, 18.66f, 6.75f, 18.25f)
|
||||||
|
verticalLineTo(5.75f)
|
||||||
|
curveTo(6.75f, 5.34f, 7.09f, 5f, 7.5f, 5f)
|
||||||
|
curveTo(7.91f, 5f, 8.25f, 5.34f, 8.25f, 5.75f)
|
||||||
|
verticalLineTo(18.25f)
|
||||||
|
curveTo(8.25f, 18.66f, 7.91f, 19f, 7.5f, 19f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
path(fill = SolidColor(Color.White)) {
|
||||||
|
moveTo(12f, 21.5f)
|
||||||
|
curveTo(11.59f, 21.5f, 11.25f, 21.16f, 11.25f, 20.75f)
|
||||||
|
verticalLineTo(3.25f)
|
||||||
|
curveTo(11.25f, 2.84f, 11.59f, 2.5f, 12f, 2.5f)
|
||||||
|
curveTo(12.41f, 2.5f, 12.75f, 2.84f, 12.75f, 3.25f)
|
||||||
|
verticalLineTo(20.75f)
|
||||||
|
curveTo(12.75f, 21.16f, 12.41f, 21.5f, 12f, 21.5f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
path(
|
||||||
|
fill = SolidColor(Color.White),
|
||||||
|
fillAlpha = 0.4f,
|
||||||
|
strokeAlpha = 0.4f
|
||||||
|
) {
|
||||||
|
moveTo(16.5f, 19f)
|
||||||
|
curveTo(16.09f, 19f, 15.75f, 18.66f, 15.75f, 18.25f)
|
||||||
|
verticalLineTo(5.75f)
|
||||||
|
curveTo(15.75f, 5.34f, 16.09f, 5f, 16.5f, 5f)
|
||||||
|
curveTo(16.91f, 5f, 17.25f, 5.34f, 17.25f, 5.75f)
|
||||||
|
verticalLineTo(18.25f)
|
||||||
|
curveTo(17.25f, 18.66f, 16.91f, 19f, 16.5f, 19f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
path(fill = SolidColor(Color.White)) {
|
||||||
|
moveTo(21f, 16.5f)
|
||||||
|
curveTo(20.59f, 16.5f, 20.25f, 16.16f, 20.25f, 15.75f)
|
||||||
|
verticalLineTo(8.25f)
|
||||||
|
curveTo(20.25f, 7.84f, 20.59f, 7.5f, 21f, 7.5f)
|
||||||
|
curveTo(21.41f, 7.5f, 21.75f, 7.84f, 21.75f, 8.25f)
|
||||||
|
verticalLineTo(15.75f)
|
||||||
|
curveTo(21.75f, 16.16f, 21.41f, 16.5f, 21f, 16.5f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return _IconsaxSoundTwotone!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ObjectPropertyName")
|
||||||
|
private var _IconsaxSoundTwotone: ImageVector? = null
|
||||||
@ -26,5 +26,5 @@ toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/402983f310a88ac6
|
|||||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/e50b80b5a11d194a898bc3e6211b7c4b/redirect
|
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/e50b80b5a11d194a898bc3e6211b7c4b/redirect
|
||||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/476d3c08f4989328dee56d22e202d98d/redirect
|
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/476d3c08f4989328dee56d22e202d98d/redirect
|
||||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/5a88b04b5e582b332d2e6bc12b45f1b9/redirect
|
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/5a88b04b5e582b332d2e6bc12b45f1b9/redirect
|
||||||
toolchainVendor=ADOPTIUM
|
toolchainVendor=JETBRAINS
|
||||||
toolchainVersion=21
|
toolchainVersion=21
|
||||||
Loading…
Reference in New Issue
Block a user