diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt index ecdf5951..ca77a3c9 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt @@ -85,7 +85,7 @@ val navigationModule = module { LibraryScreen() } navigation { - SettingsScreen(pluginManager = get(), settingsViewModel = koinViewModel()) + SettingsScreen(settingsViewModel = koinViewModel()) } navigation { PluginScreen(pluginManager = get()) diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt index d58e3444..c4b7190c 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt @@ -17,10 +17,12 @@ package dev.krtirtho.spotube.modules.plugin +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -29,6 +31,10 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -46,13 +52,23 @@ 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.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import compose.icons.FeatherIcons +import compose.icons.feathericons.Activity +import compose.icons.feathericons.AlignLeft +import compose.icons.feathericons.Check +import compose.icons.feathericons.ExternalLink +import compose.icons.feathericons.FileText +import compose.icons.feathericons.Music import compose.icons.feathericons.Package import dev.krtirtho.spotube.PlatformType +import dev.krtirtho.spotube.core.ui.component.AdaptiveDropdownBottomSheet +import dev.krtirtho.spotube.core.ui.component.AdaptiveMenuItem import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar +import dev.krtirtho.spotube.core.ui.component.HeaderDisplayMode import dev.krtirtho.spotube.core.webview.WebViewController import dev.krtirtho.spotube.getPlatform import dev.krtirtho.spotube.modules.plugin.components.InstallSection @@ -65,6 +81,7 @@ import io.github.vinceglb.filekit.readBytes import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource import org.koin.compose.koinInject +import kotlinx.coroutines.flow.StateFlow import spotube.composeapp.generated.resources.Res import spotube.composeapp.generated.resources.plugin_empty_subtitle import spotube.composeapp.generated.resources.plugin_empty_title @@ -75,6 +92,18 @@ import spotube.composeapp.generated.resources.plugin_installed_count import spotube.composeapp.generated.resources.plugin_installed_plural import spotube.composeapp.generated.resources.plugin_installed_singular import spotube.composeapp.generated.resources.plugin_screen_title +import spotube.composeapp.generated.resources.settings_plugins_action_change +import spotube.composeapp.generated.resources.settings_plugins_action_select +import spotube.composeapp.generated.resources.settings_plugins_ability_audio +import spotube.composeapp.generated.resources.settings_plugins_ability_lyrics +import spotube.composeapp.generated.resources.settings_plugins_ability_metadata +import spotube.composeapp.generated.resources.settings_plugins_ability_scrobble +import spotube.composeapp.generated.resources.settings_plugins_clear +import spotube.composeapp.generated.resources.settings_plugins_default_ability_title +import spotube.composeapp.generated.resources.settings_plugins_manage_title +import spotube.composeapp.generated.resources.settings_plugins_no_plugins +import spotube.composeapp.generated.resources.settings_plugins_no_selection +import spotube.composeapp.generated.resources.settings_plugins_plugin_content_description @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -189,6 +218,26 @@ fun PluginScreen( ) } + // ── Default ability plugin selectors ───────────────── + items(PluginAbility.entries.size) { index -> + val ability = PluginAbility.entries[index] + val selectedPlugin = state.selectedPlugins[ability] + DefaultAbilityPluginSelector( + ability = ability, + selectedPlugin = selectedPlugin, + state = when (ability) { + PluginAbility.METADATA -> pluginManager.metadataPlugins + PluginAbility.AUDIO -> pluginManager.audioPlugins + PluginAbility.LYRICS -> pluginManager.lyricsPlugins + PluginAbility.SCROBBLE -> pluginManager.scrobblePlugins + }, + onSelected = { plugin -> + pluginManager.setSelectedPlugin(ability, plugin) + }, + onManagePlugins = { } + ) + } + if (state.plugins.isEmpty()) { item { Box( @@ -314,3 +363,253 @@ fun PluginScreen( } } +@Composable +fun DefaultAbilityPluginSelector( + ability: PluginAbility, + state: StateFlow>, + selectedPlugin: PluginEntry? = null, + onSelected: (PluginEntry?) -> Unit = { }, + onManagePlugins: () -> Unit = { } +) { + val plugins by state.collectAsStateWithLifecycle() + val noPluginsText = stringResource(Res.string.settings_plugins_no_plugins) + val clearText = stringResource(Res.string.settings_plugins_clear) + val manageText = stringResource(Res.string.settings_plugins_manage_title) + + val menuItems = buildList { + if (plugins.isNotEmpty()) { + plugins.forEach { plugin -> + val isSelected = selectedPlugin?.name == plugin.name + add( + AdaptiveMenuItem( + label = plugin.name, + onClick = { onSelected(plugin) }, + selected = isSelected, + ) + ) + } + + if (selectedPlugin != null) { + add( + AdaptiveMenuItem( + icon = FeatherIcons.AlignLeft, + label = clearText, + onClick = { onSelected(null) }, + dividerBefore = true, + ) + ) + } + } else { + add( + AdaptiveMenuItem( + label = noPluginsText, + onClick = { }, + enabled = false, + ) + ) + } + + add( + AdaptiveMenuItem( + icon = FeatherIcons.ExternalLink, + label = manageText, + onClick = onManagePlugins, + dividerBefore = true, + ) + ) + } + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 4.dp) + ) { + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f) + ), + border = BorderStroke( + width = 1.dp, + color = if (selectedPlugin != null) { + MaterialTheme.colorScheme.primary.copy(alpha = 0.3f) + } else { + MaterialTheme.colorScheme.outlineVariant + } + ) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(12.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + modifier = Modifier.weight(1f), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Surface( + modifier = Modifier.clip(RoundedCornerShape(8.dp)), + color = when (ability) { + PluginAbility.METADATA -> Color(0xFF4CAF50).copy(alpha = 0.1f) + PluginAbility.AUDIO -> Color(0xFF2196F3).copy(alpha = 0.1f) + PluginAbility.LYRICS -> Color(0xFFFFC107).copy(alpha = 0.1f) + PluginAbility.SCROBBLE -> Color(0xFF9C27B0).copy(alpha = 0.1f) + } + ) { + Icon( + imageVector = when (ability) { + PluginAbility.METADATA -> FeatherIcons.FileText + PluginAbility.AUDIO -> FeatherIcons.Music + PluginAbility.LYRICS -> FeatherIcons.AlignLeft + PluginAbility.SCROBBLE -> FeatherIcons.Activity + }, + contentDescription = stringResource( + Res.string.settings_plugins_plugin_content_description, + ability.displayLabel() + ), + modifier = Modifier.padding(8.dp), + tint = when (ability) { + PluginAbility.METADATA -> Color(0xFF4CAF50) + PluginAbility.AUDIO -> Color(0xFF2196F3) + PluginAbility.LYRICS -> Color(0xFFFFC107) + PluginAbility.SCROBBLE -> Color(0xFF9C27B0) + } + ) + } + + Column(modifier = Modifier.weight(1f)) { + Text( + stringResource( + Res.string.settings_plugins_default_ability_title, + ability.displayLabel() + ), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurface + ) + if (selectedPlugin != null) { + Text( + selectedPlugin.name, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(top = 4.dp) + ) + } else { + Text( + stringResource(Res.string.settings_plugins_no_selection), + style = MaterialTheme.typography.bodySmall, + color = Color.Gray, + modifier = Modifier.padding(top = 4.dp) + ) + } + } + } + + AdaptiveDropdownBottomSheet( + items = menuItems, + headerDisplayMode = HeaderDisplayMode.OnlyInBottomSheet, + header = { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Surface( + modifier = Modifier.clip(RoundedCornerShape(8.dp)), + color = when (ability) { + PluginAbility.METADATA -> Color(0xFF4CAF50).copy(alpha = 0.1f) + PluginAbility.AUDIO -> Color(0xFF2196F3).copy(alpha = 0.1f) + PluginAbility.LYRICS -> Color(0xFFFFC107).copy(alpha = 0.1f) + PluginAbility.SCROBBLE -> Color(0xFF9C27B0).copy(alpha = 0.1f) + } + ) { + Icon( + imageVector = when (ability) { + PluginAbility.METADATA -> FeatherIcons.FileText + PluginAbility.AUDIO -> FeatherIcons.Music + PluginAbility.LYRICS -> FeatherIcons.AlignLeft + PluginAbility.SCROBBLE -> FeatherIcons.Activity + }, + contentDescription = null, + modifier = Modifier.padding(8.dp), + tint = when (ability) { + PluginAbility.METADATA -> Color(0xFF4CAF50) + PluginAbility.AUDIO -> Color(0xFF2196F3) + PluginAbility.LYRICS -> Color(0xFFFFC107) + PluginAbility.SCROBBLE -> Color(0xFF9C27B0) + } + ) + } + Column(modifier = Modifier.weight(1f)) { + Text( + stringResource( + Res.string.settings_plugins_default_ability_title, + ability.displayLabel() + ), + style = MaterialTheme.typography.titleMedium, + ) + selectedPlugin?.let { + Text( + it.name, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.primary, + ) + } + } + } + }, + trigger = { onClick -> + Button( + onClick = onClick, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f), + contentColor = MaterialTheme.colorScheme.primary + ), + modifier = Modifier.clip(RoundedCornerShape(8.dp)) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = FeatherIcons.Check, + contentDescription = null, + modifier = Modifier.padding(0.dp), + tint = MaterialTheme.colorScheme.primary + ) + Text( + if (selectedPlugin != null) { + stringResource(Res.string.settings_plugins_action_change) + } else { + stringResource(Res.string.settings_plugins_action_select) + }, + style = MaterialTheme.typography.labelSmall + ) + } + } + }, + ) + } + } + } + } +} + +@Composable +private fun PluginAbility.displayLabel(): String { + return when (this) { + PluginAbility.METADATA -> stringResource(Res.string.settings_plugins_ability_metadata) + PluginAbility.AUDIO -> stringResource(Res.string.settings_plugins_ability_audio) + PluginAbility.LYRICS -> stringResource(Res.string.settings_plugins_ability_lyrics) + PluginAbility.SCROBBLE -> stringResource(Res.string.settings_plugins_ability_scrobble) + } +} + diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/SettingsScreen.kt index fc731f8d..b393075d 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/SettingsScreen.kt @@ -37,7 +37,6 @@ import dev.krtirtho.spotube.PlatformType import dev.krtirtho.spotube.getPlatform import dev.krtirtho.spotube.core.navigation.NavigationCommands import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar -import dev.krtirtho.spotube.modules.plugin.PluginManager import spotube.composeapp.generated.resources.* import dev.krtirtho.spotube.modules.settings.sections.appearanceSection import dev.krtirtho.spotube.modules.settings.sections.cacheSection @@ -54,9 +53,8 @@ import org.koin.compose.koinInject @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SettingsScreen(pluginManager: PluginManager, settingsViewModel: SettingsViewModel) { +fun SettingsScreen(settingsViewModel: SettingsViewModel) { val navigatorCommands = koinInject() - val pluginState by pluginManager.state.collectAsStateWithLifecycle() val settingsState by settingsViewModel.settingsState.collectAsStateWithLifecycle() val platformType = remember { getPlatform().type } val isDesktopPlatform = platformType == PlatformType.Windows || @@ -90,8 +88,6 @@ fun SettingsScreen(pluginManager: PluginManager, settingsViewModel: SettingsView contentPadding = contentPadding, ) { pluginsSection( - pluginManager = pluginManager, - pluginState = pluginState, navigatorCommands = navigatorCommands, ) if (settingsState != null) { diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/sections/PluginsSection.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/sections/PluginsSection.kt index 93c190eb..32aa87cc 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/sections/PluginsSection.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/settings/sections/PluginsSection.kt @@ -17,299 +17,29 @@ package dev.krtirtho.spotube.modules.settings.sections -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle import compose.icons.FeatherIcons -import compose.icons.feathericons.Activity -import compose.icons.feathericons.AlignLeft -import compose.icons.feathericons.Check import compose.icons.feathericons.ChevronRight -import compose.icons.feathericons.ExternalLink -import compose.icons.feathericons.FileText -import compose.icons.feathericons.Music import compose.icons.feathericons.Package import dev.krtirtho.spotube.core.navigation.NavigationCommands import dev.krtirtho.spotube.core.navigation.Routes -import dev.krtirtho.spotube.core.ui.component.AdaptiveDropdownBottomSheet -import dev.krtirtho.spotube.core.ui.component.AdaptiveMenuItem -import dev.krtirtho.spotube.core.ui.component.HeaderDisplayMode import spotube.composeapp.generated.resources.* -import dev.krtirtho.spotube.modules.plugin.PluginAbility -import dev.krtirtho.spotube.modules.plugin.PluginEntry -import dev.krtirtho.spotube.modules.plugin.PluginManager -import dev.krtirtho.spotube.modules.plugin.PluginManagerStates import dev.krtirtho.spotube.modules.settings.components.SettingCardItem -import kotlinx.coroutines.flow.StateFlow import org.jetbrains.compose.resources.stringResource -@Composable -fun DefaultAbilityPluginSelector( - ability: PluginAbility, - state: StateFlow>, - selectedPlugin: PluginEntry? = null, - onSelected: (PluginEntry?) -> Unit = { }, - onManagePlugins: () -> Unit = { } -) { - val plugins by state.collectAsStateWithLifecycle() - val noPluginsText = stringResource(Res.string.settings_plugins_no_plugins) - val clearText = stringResource(Res.string.settings_plugins_clear) - val manageText = stringResource(Res.string.settings_plugins_manage_title) - - val menuItems = buildList { - if (plugins.isNotEmpty()) { - plugins.forEach { plugin -> - val isSelected = selectedPlugin?.name == plugin.name - add( - AdaptiveMenuItem( - label = plugin.name, - onClick = { onSelected(plugin) }, - selected = isSelected, - ) - ) - } - - if (selectedPlugin != null) { - add( - AdaptiveMenuItem( - icon = FeatherIcons.AlignLeft, - label = clearText, - onClick = { onSelected(null) }, - dividerBefore = true, - ) - ) - } - } else { - add( - AdaptiveMenuItem( - label = noPluginsText, - onClick = { }, - enabled = false, - ) - ) - } - - add( - AdaptiveMenuItem( - icon = FeatherIcons.ExternalLink, - label = manageText, - onClick = onManagePlugins, - dividerBefore = true, - ) - ) - } - - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 8.dp, vertical = 4.dp) - ) { - Card( - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f) - ), - border = BorderStroke( - width = 1.dp, - color = if (selectedPlugin != null) { - MaterialTheme.colorScheme.primary.copy(alpha = 0.3f) - } else { - MaterialTheme.colorScheme.outlineVariant - } - ) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(12.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Row( - modifier = Modifier.weight(1f), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp) - ) { - Surface( - modifier = Modifier.clip(RoundedCornerShape(8.dp)), - color = when (ability) { - PluginAbility.METADATA -> Color(0xFF4CAF50).copy(alpha = 0.1f) - PluginAbility.AUDIO -> Color(0xFF2196F3).copy(alpha = 0.1f) - PluginAbility.LYRICS -> Color(0xFFFFC107).copy(alpha = 0.1f) - PluginAbility.SCROBBLE -> Color(0xFF9C27B0).copy(alpha = 0.1f) - } - ) { - Icon( - imageVector = when (ability) { - PluginAbility.METADATA -> FeatherIcons.FileText - PluginAbility.AUDIO -> FeatherIcons.Music - PluginAbility.LYRICS -> FeatherIcons.AlignLeft - PluginAbility.SCROBBLE -> FeatherIcons.Activity - }, - contentDescription = stringResource( - Res.string.settings_plugins_plugin_content_description, - ability.displayLabel() - ), - modifier = Modifier.padding(8.dp), - tint = when (ability) { - PluginAbility.METADATA -> Color(0xFF4CAF50) - PluginAbility.AUDIO -> Color(0xFF2196F3) - PluginAbility.LYRICS -> Color(0xFFFFC107) - PluginAbility.SCROBBLE -> Color(0xFF9C27B0) - } - ) - } - - Column(modifier = Modifier.weight(1f)) { - Text( - stringResource( - Res.string.settings_plugins_default_ability_title, - ability.displayLabel() - ), - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onSurface - ) - if (selectedPlugin != null) { - Text( - selectedPlugin.name, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.primary, - modifier = Modifier.padding(top = 4.dp) - ) - } else { - Text( - stringResource(Res.string.settings_plugins_no_selection), - style = MaterialTheme.typography.bodySmall, - color = Color.Gray, - modifier = Modifier.padding(top = 4.dp) - ) - } - } - } - - AdaptiveDropdownBottomSheet( - items = menuItems, - headerDisplayMode = HeaderDisplayMode.OnlyInBottomSheet, - header = { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - Surface( - modifier = Modifier.clip(RoundedCornerShape(8.dp)), - color = when (ability) { - PluginAbility.METADATA -> Color(0xFF4CAF50).copy(alpha = 0.1f) - PluginAbility.AUDIO -> Color(0xFF2196F3).copy(alpha = 0.1f) - PluginAbility.LYRICS -> Color(0xFFFFC107).copy(alpha = 0.1f) - PluginAbility.SCROBBLE -> Color(0xFF9C27B0).copy(alpha = 0.1f) - } - ) { - Icon( - imageVector = when (ability) { - PluginAbility.METADATA -> FeatherIcons.FileText - PluginAbility.AUDIO -> FeatherIcons.Music - PluginAbility.LYRICS -> FeatherIcons.AlignLeft - PluginAbility.SCROBBLE -> FeatherIcons.Activity - }, - contentDescription = null, - modifier = Modifier.padding(8.dp), - tint = when (ability) { - PluginAbility.METADATA -> Color(0xFF4CAF50) - PluginAbility.AUDIO -> Color(0xFF2196F3) - PluginAbility.LYRICS -> Color(0xFFFFC107) - PluginAbility.SCROBBLE -> Color(0xFF9C27B0) - } - ) - } - Column(modifier = Modifier.weight(1f)) { - Text( - stringResource( - Res.string.settings_plugins_default_ability_title, - ability.displayLabel() - ), - style = MaterialTheme.typography.titleMedium, - ) - selectedPlugin?.let { - Text( - it.name, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.primary, - ) - } - } - } - }, - trigger = { onClick -> - Button( - onClick = onClick, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f), - contentColor = MaterialTheme.colorScheme.primary - ), - modifier = Modifier.clip(RoundedCornerShape(8.dp)) - ) { - Row( - horizontalArrangement = Arrangement.spacedBy(6.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector = FeatherIcons.Check, - contentDescription = null, - modifier = Modifier.padding(0.dp), - tint = MaterialTheme.colorScheme.primary - ) - Text( - if (selectedPlugin != null) { - stringResource(Res.string.settings_plugins_action_change) - } else { - stringResource(Res.string.settings_plugins_action_select) - }, - style = MaterialTheme.typography.labelSmall - ) - } - } - }, - ) - } - } - } - } -} - internal fun LazyListScope.pluginsSection( - pluginManager: PluginManager, - pluginState: PluginManagerStates, navigatorCommands: NavigationCommands ) { item { @@ -351,35 +81,4 @@ internal fun LazyListScope.pluginsSection( } ) } - items(PluginAbility.entries.size) { index -> - val ability = PluginAbility.entries[index] - val selectedPlugin = - (pluginState as? PluginManagerStates.Data)?.selectedPlugins?.get(ability) - DefaultAbilityPluginSelector( - ability = ability, - selectedPlugin = selectedPlugin, - state = when (ability) { - PluginAbility.METADATA -> pluginManager.metadataPlugins - PluginAbility.AUDIO -> pluginManager.audioPlugins - PluginAbility.LYRICS -> pluginManager.lyricsPlugins - PluginAbility.SCROBBLE -> pluginManager.scrobblePlugins - }, - onSelected = { plugin -> - pluginManager.setSelectedPlugin(ability, plugin) - }, - onManagePlugins = { - navigatorCommands.navigateTo(Routes.Plugins) - } - ) - } -} - -@Composable -private fun PluginAbility.displayLabel(): String { - return when (this) { - PluginAbility.METADATA -> stringResource(Res.string.settings_plugins_ability_metadata) - PluginAbility.AUDIO -> stringResource(Res.string.settings_plugins_ability_audio) - PluginAbility.LYRICS -> stringResource(Res.string.settings_plugins_ability_lyrics) - PluginAbility.SCROBBLE -> stringResource(Res.string.settings_plugins_ability_scrobble) - } }