feat(navigation): refactor sidebar and bottombar navigation structure with new tab and group models

This commit is contained in:
Kingkor Roy Tirtho 2026-09-25 21:07:19 +06:00
parent 78168d269c
commit 86ec117cfd
9 changed files with 483 additions and 238 deletions

View File

@ -40,56 +40,91 @@ import dev.krtirtho.spotube.core.navigation.rememberNavigationState
import dev.krtirtho.spotube.core.navigation.toEntries
import dev.krtirtho.spotube.core.ui.component.LocalSharedTransitionScope
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.UserSettings
import dev.krtirtho.spotube.modules.shell.AppShell
import dev.krtirtho.spotube.modules.webview.WebViewScreen
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.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.IconsaxMusicLibraryOutline
import dev.krtirtho.spotube.resources.iconsax.IconsaxSearch
import dev.krtirtho.spotube.resources.iconsax.IconsaxSearchBroken
import dev.krtirtho.spotube.resources.iconsax.IconsaxSetting2
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 org.koin.compose.koinInject
import org.koin.compose.navigation3.koinEntryProvider
import org.koin.core.annotation.KoinExperimentalAPI
data class TabItem(
val title: String,
val icon: ImageVector,
val activeIcon: ImageVector,
val route: Routes
interface NavigationItem {
data class Tab<T : Any>(
val title: String, val icon: ImageVector, val route: Routes,
val data: T? = null
) : 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(
TabItem(
"Home",
Iconsax.IconsaxHomeBroken,
Iconsax.IconsaxHome,
Routes.Home
),
TabItem(
"Search",
Iconsax.IconsaxSearchBroken,
Iconsax.IconsaxSearch,
Routes.Search
),
TabItem(
val sidebarTabs = tabs + listOf(
NavigationItem.Group(
"Library",
Iconsax.IconsaxMusicLibraryOutline,
Iconsax.IconsaxMusicLibrary,
Routes.Library
items = listOf(
NavigationItem.Tab(
"Playlists",
Iconsax.IconsaxMusicDashboard,
Routes.Library,
LibraryTab.Playlists
),
TabItem(
"Settings",
Iconsax.IconsaxSettingTwotone,
Iconsax.IconsaxSetting2,
Routes.Settings
NavigationItem.Tab("Albums", Iconsax.IconsaxCd, Routes.Library, LibraryTab.Albums),
NavigationItem.Tab("Artists", Iconsax.User, Routes.Library, LibraryTab.Artists),
)
),
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(
@ -105,8 +140,7 @@ fun App(
val userSettings by settingsRepository.userSettings.collectAsStateWithLifecycle(initialValue = UserSettings())
val navigationState = rememberNavigationState(
startRoute = Routes.Home,
topLevelRoutes = TOP_LEVEL_ROUTES
startRoute = Routes.Home, topLevelRoutes = TOP_LEVEL_ROUTES
)
val navigator = remember {
Navigator(navigationState)

View File

@ -38,10 +38,16 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Scaffold
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.LaunchedEffect
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.misc.SkeletonTree
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.IconsaxAddSquare
import dev.krtirtho.spotube.resources.iconsax.IconsaxPlay
@ -134,7 +141,8 @@ fun ArtistScreen(
state = listState,
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
.padding(innerPadding)
.padding(bottom = LocalAppShellBottomInset.current + 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
item {
@ -495,6 +503,7 @@ private fun ArtistMeta(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ArtistHeaderActions(
isSaved: Boolean,
@ -508,7 +517,12 @@ private fun ArtistHeaderActions(
) {
if (isSaved) {
SecondaryButton(onClick = onFollowClick) {
Text("Following", modifier = Modifier.width(65.dp), textAlign = TextAlign.Center)
Text(
"Following",
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
} else {
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) {
SecondaryIconButton(onClick = onBlacklistClick) {
Icon(
@ -533,6 +556,7 @@ private fun ArtistHeaderActions(
}
}
}
}
}
@Composable

View File

@ -54,6 +54,8 @@ import androidx.compose.ui.unit.sp
import compose.icons.FeatherIcons
import compose.icons.feathericons.X
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.IconsaxArrowDown4
import kotlinx.coroutines.launch
@ -70,18 +72,12 @@ fun LyricsScreen(
val scope = rememberCoroutineScope()
Scaffold(
modifier = modifier,
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(horizontal = 16.dp)
) {
Spacer(modifier = Modifier.height(16.dp))
topBar = {
ApplicationMainBar(
backButton = onClose == null,
title = {
Box(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
) {
if (onClose != null) {
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 {
uiState.isLoading -> {
Box(

View File

@ -42,11 +42,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
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.Navigator
import dev.krtirtho.spotube.core.navigation.Routes
import dev.krtirtho.spotube.modules.downloads.DownloadBadgeIndicator
import dev.krtirtho.spotube.tabs
@Composable
fun AppBottombar(
@ -68,8 +69,9 @@ fun AppBottombar(
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
tabs.forEach { (label, icon, activeIcon, screen) ->
val selected = navigationState.topLevelRoute == screen
bottombarTabs.filterIsInstance<NavigationItem.Tab<*>>()
.forEach { tab ->
val selected = navigationState.topLevelRoute == tab.route
val labelColor = if (selected) {
MaterialTheme.colorScheme.primary
} else {
@ -79,7 +81,7 @@ fun AppBottombar(
Column(
modifier = Modifier
.clip(CircleShape)
.clickable { navigator.navigate(screen) }
.clickable { navigator.navigate(tab.route) }
.padding(8.dp)
.size(56.dp),
horizontalAlignment = Alignment.CenterHorizontally,
@ -87,13 +89,13 @@ fun AppBottombar(
) {
Box {
Icon(
imageVector = activeIcon,
contentDescription = label,
imageVector = tab.icon,
contentDescription = tab.title,
tint = if (selected) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(20.dp)
)
if (screen == Routes.Library) {
if (tab.route == Routes.Library) {
DownloadBadgeIndicator(
modifier = Modifier.align(Alignment.TopEnd)
)
@ -101,7 +103,7 @@ fun AppBottombar(
}
Spacer(modifier = Modifier.height(4.dp))
Text(
text = label,
text = tab.title,
style = MaterialTheme.typography.labelSmall,
fontWeight = if (selected) FontWeight.Medium else FontWeight.Normal,
color = labelColor,

View File

@ -29,12 +29,16 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
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.blurEffect
import dev.chrisbanes.haze.hazeEffect
import dev.krtirtho.spotube.NavigationItem
import dev.krtirtho.spotube.core.navigation.NavigationState
import dev.krtirtho.spotube.core.navigation.Navigator
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.copyPadding
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.library.LibraryState
import dev.krtirtho.spotube.modules.library.LibraryTab
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.IconsaxSidebarRightBroken
import dev.krtirtho.spotube.resources.iconsax.User
import dev.krtirtho.spotube.tabs
import dev.krtirtho.spotube.sidebarTabs
import org.jetbrains.compose.resources.Font
import org.koin.compose.koinInject
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 currentLibraryTab by libraryState.currentTab.collectAsState()
val surfaceTint = MaterialTheme.colorScheme.surface.copy(alpha = 0.85f)
val listState = rememberLazyListState()
Column(
modifier = modifier
@ -139,62 +145,74 @@ fun AppSidebar(
Spacer(modifier = Modifier.height(8.dp))
tabs.forEach { (label, icon, activeIcon, screen) ->
val selected = navigationState.topLevelRoute == screen
if (screen == Routes.Library) {
AnimatedVisibility(visible = expanded) {
Box(modifier = Modifier.fillMaxSize()) {
LazyColumn(state = listState) {
items(sidebarTabs.size) { index ->
when (val tab = sidebarTabs[index]) {
is NavigationItem.Group<*> -> {
androidx.compose.animation.AnimatedVisibility(visible = expanded) {
Text(
text = "LIBRARY",
text = tab.title.uppercase(),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.fillMaxWidth()
.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(
label = tab.title,
activeIcon = tab.icon,
onClick = {
libraryState.currentTab.value = tab
navigator.navigate(screen)
navigator.navigate(tab.route)
},
selected = isSubSelected,
expanded = expanded,
showDownloadBadge = tab == LibraryTab.Downloads,
)
}
} else {
SidebarItem(
label = label,
activeIcon = activeIcon,
onClick = { navigator.navigate(screen) },
selected = selected,
expanded = expanded,
)
}
}
}
}
VerticalScrollbar(
listState = listState,
modifier = Modifier
.align(Alignment.CenterEnd)
.fillMaxHeight()
)
}
}
HorizontalDivider()
SidebarItem(
label = "Devices",
activeIcon = Iconsax.IconsaxMirroringScreen,
onClick = { navigator.navigate(Routes.Devices) },
selected = false,
label = "Settings",
activeIcon = Iconsax.IconsaxSetting2,
onClick = {
navigator.navigate(Routes.Settings)
},
selected = navigationState.topLevelRoute == Routes.Settings,
expanded = expanded,
)
SidebarItem(
label = "Group Jam",
activeIcon = Iconsax.User,
onClick = { navigator.navigate(Routes.Jam) },
selected = false,
expanded = expanded,
)
Spacer(modifier = Modifier.height(120.dp))
}
}
@ -209,7 +227,7 @@ fun SidebarItem(
showDownloadBadge: Boolean = false,
) {
val itemContent: @Composable RowScope.() -> Unit = {
Box(modifier = Modifier.size(20.dp)) {
Box(modifier = Modifier.size(16.dp)) {
Icon(
imageVector = activeIcon,
contentDescription = label,
@ -231,7 +249,7 @@ fun SidebarItem(
maxLines = 1,
softWrap = false,
style = LocalTextStyle.current.copy(
fontSize = MaterialTheme.typography.labelLarge.fontSize,
fontSize = MaterialTheme.typography.labelMedium.fontSize,
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Medium,
color = if (selected) {
MaterialTheme.colorScheme.onSecondaryContainer
@ -246,8 +264,8 @@ fun SidebarItem(
val buttonModifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp, vertical = 4.dp)
val contentPadding = PaddingValues(horizontal = 7.dp, vertical = 6.dp)
.padding(horizontal = 7.dp, vertical = 2.dp)
val contentPadding = PaddingValues(horizontal = 5.dp)
if (selected) {
SecondaryButton(
@ -255,7 +273,7 @@ fun SidebarItem(
modifier = buttonModifier,
theme = LocalBaseUITheme.current.buttons.secondary
.copyPadding(contentPadding)
.copyShape(RoundedCornerShape(8.dp)),
.copyShape(RoundedCornerShape(5.dp)),
content = itemContent,
)
} else {
@ -264,7 +282,7 @@ fun SidebarItem(
modifier = buttonModifier,
theme = LocalBaseUITheme.current.buttons.outline
.copyPadding(contentPadding)
.copyShape(RoundedCornerShape(8.dp)),
.copyShape(RoundedCornerShape(5.dp)),
hoverOnly = true,
content = itemContent,
)

View File

@ -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

View File

@ -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
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)
close()
}
path(
fill = SolidColor(Color.White),
fillAlpha = 0.4f,
strokeAlpha = 0.4f
) {
path(fill = SolidColor(Color.White)) {
moveTo(7.5f, 19f)
curveTo(7.09f, 19f, 6.75f, 18.66f, 6.75f, 18.25f)
verticalLineTo(5.75f)
@ -80,11 +59,7 @@ val Iconsax.IconsaxSound: ImageVector
curveTo(12.75f, 21.16f, 12.41f, 21.5f, 12f, 21.5f)
close()
}
path(
fill = SolidColor(Color.White),
fillAlpha = 0.4f,
strokeAlpha = 0.4f
) {
path(fill = SolidColor(Color.White)) {
moveTo(16.5f, 19f)
curveTo(16.09f, 19f, 15.75f, 18.66f, 15.75f, 18.25f)
verticalLineTo(5.75f)

View File

@ -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

View File

@ -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.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
toolchainVendor=ADOPTIUM
toolchainVendor=JETBRAINS
toolchainVersion=21