mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-07-26 20:02:14 +00:00
Redesign Plugins screen to match Addons screen visuals, rename from Scraper Plugins
Scrapers are now grouped under their owning repository instead of two separate flat lists, and the screen reuses AddonStoreScreen's visual language (icon-circle back button, bordered repo cards, icon buttons) instead of its own bespoke plain-text styling. Adds a refreshRepository action reusing the existing re-fetch dispatch.
This commit is contained in:
parent
1e776bbb8a
commit
28501ff31b
5 changed files with 290 additions and 209 deletions
|
|
@ -133,6 +133,11 @@ class PluginRepositoryManager @Inject constructor(
|
|||
persistCurrentState()
|
||||
}
|
||||
|
||||
suspend fun refreshRepository(manifestUrl: String) {
|
||||
runtime.dispatch(mapOf("type" to "pluginRepositoryAddRequested", "manifestUrl" to manifestUrl))
|
||||
persistCurrentState()
|
||||
}
|
||||
|
||||
suspend fun toggleScraper(scraperId: String, enabled: Boolean) {
|
||||
runtime.dispatch(mapOf("type" to "pluginScraperToggled", "scraperId" to scraperId, "enabled" to enabled))
|
||||
persistCurrentState()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.fluxa.app.ui.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
|
@ -13,17 +15,24 @@ 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.rememberScrollState
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -33,14 +42,14 @@ 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.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material3.Icon
|
||||
import com.fluxa.app.common.AppStrings
|
||||
import com.fluxa.app.plugins.PluginRepositoryUiModel
|
||||
import com.fluxa.app.plugins.PluginScraperUiModel
|
||||
|
|
@ -59,6 +68,7 @@ fun PluginsSettingsRoute(
|
|||
language = language,
|
||||
onAddRepository = viewModel::addRepository,
|
||||
onRemoveRepository = viewModel::removeRepository,
|
||||
onRefreshRepository = viewModel::refreshRepository,
|
||||
onToggleScraper = viewModel::toggleScraper,
|
||||
onOpenScraperSettings = viewModel::openScraperSettings,
|
||||
onBackRequested = onBackRequested
|
||||
|
|
@ -84,155 +94,233 @@ fun PluginsSettingsScreen(
|
|||
language: String?,
|
||||
onAddRepository: (String) -> Unit,
|
||||
onRemoveRepository: (String) -> Unit,
|
||||
onRefreshRepository: (String) -> Unit,
|
||||
onToggleScraper: (String, Boolean) -> Unit,
|
||||
onOpenScraperSettings: (PluginScraperUiModel) -> Unit,
|
||||
onBackRequested: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var showAddDialog by remember { mutableStateOf(false) }
|
||||
val scrapersByRepository = state.scrapers.groupBy { it.repositoryUrl }
|
||||
|
||||
Box(modifier = modifier.fillMaxSize().background(FluxaColors.background)) {
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 20.dp)) {
|
||||
PluginsTopBar(title = AppStrings.t(language, "settings.plugins.title"), onBack = onBackRequested)
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
|
||||
PluginsSectionHeader(AppStrings.t(language, "settings.plugins.repositories"))
|
||||
PluginsActionRow(AppStrings.t(language, "settings.plugins.add_repository")) { showAddDialog = true }
|
||||
|
||||
if (state.addingRepositoryUrl != null) {
|
||||
Text(
|
||||
AppStrings.t(language, "settings.plugins.adding"),
|
||||
color = Color.White.copy(alpha = 0.6f),
|
||||
fontSize = 13.sp,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
state.error?.let { error ->
|
||||
Text(
|
||||
AppStrings.format(language, "settings.plugins.error", error),
|
||||
color = FluxaColors.errorRed,
|
||||
fontSize = 13.sp,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if (state.repositories.isEmpty()) {
|
||||
Text(
|
||||
AppStrings.t(language, "settings.plugins.no_repositories"),
|
||||
color = Color.White.copy(alpha = 0.5f),
|
||||
modifier = Modifier.padding(vertical = 12.dp)
|
||||
)
|
||||
} else {
|
||||
state.repositories.forEach { repository ->
|
||||
PluginRepositoryRow(
|
||||
repository = repository,
|
||||
language = language,
|
||||
onRemove = { onRemoveRepository(repository.manifestUrl) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(20.dp))
|
||||
PluginsSectionHeader(AppStrings.t(language, "settings.plugins.scrapers"))
|
||||
if (state.scrapers.isEmpty()) {
|
||||
Text(
|
||||
AppStrings.t(language, "settings.plugins.no_scrapers"),
|
||||
color = Color.White.copy(alpha = 0.5f),
|
||||
modifier = Modifier.padding(vertical = 12.dp)
|
||||
)
|
||||
} else {
|
||||
state.scrapers.forEach { scraper ->
|
||||
PluginScraperRow(
|
||||
scraper = scraper,
|
||||
language = language,
|
||||
onToggle = { enabled -> onToggleScraper(scraper.id, enabled) },
|
||||
onOpenSettings = { onOpenScraperSettings(scraper) }
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(120.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showAddDialog) {
|
||||
PluginAddRepositoryDialog(
|
||||
language = language,
|
||||
onDismiss = { showAddDialog = false },
|
||||
onConfirm = { url ->
|
||||
onAddRepository(url)
|
||||
showAddDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PluginsTopBar(title: String, onBack: () -> Unit) {
|
||||
Row(modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Box(
|
||||
modifier = Modifier.size(40.dp).clip(CircleShape).background(Color.White.copy(alpha = 0.05f)).clickable(onClick = onBack),
|
||||
contentAlignment = Alignment.Center
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
Text("←", color = Color.White)
|
||||
item {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(18.dp)) {
|
||||
var backFocused by remember { mutableStateOf(false) }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.onFocusChanged { backFocused = it.isFocused }
|
||||
.background(if (backFocused) Color.White else Color.White.copy(alpha = 0.05f))
|
||||
.clickable(onClick = onBackRequested),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
tint = if (backFocused) Color.Black else Color.White,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = AppStrings.t(language, "settings.plugins.title"),
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Black,
|
||||
fontSize = 26.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
PluginRepositoryInput(language = language, onSubmit = onAddRepository)
|
||||
}
|
||||
|
||||
if (state.error != null) {
|
||||
item {
|
||||
Text(
|
||||
text = AppStrings.format(language, "settings.plugins.error", state.error),
|
||||
color = FluxaColors.errorRed,
|
||||
fontSize = 13.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val addingNewRepositoryUrl = state.addingRepositoryUrl
|
||||
?.takeIf { url -> state.repositories.none { it.manifestUrl == url } }
|
||||
if (addingNewRepositoryUrl != null) {
|
||||
item {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
CircularProgressIndicator(color = Color.White.copy(alpha = 0.68f), strokeWidth = 2.dp, modifier = Modifier.size(16.dp))
|
||||
Text(
|
||||
text = AppStrings.t(language, "settings.plugins.adding"),
|
||||
color = Color.White.copy(alpha = 0.6f),
|
||||
fontSize = 13.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Text(
|
||||
text = AppStrings.t(language, "settings.plugins.installed_repositories"),
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
|
||||
if (state.repositories.isEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
text = AppStrings.t(language, "settings.plugins.no_repositories"),
|
||||
color = Color.White.copy(alpha = 0.5f),
|
||||
fontSize = 13.sp,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items(state.repositories, key = { "repo:${it.manifestUrl}" }) { repository ->
|
||||
PluginRepositoryGroup(
|
||||
repository = repository,
|
||||
scrapers = scrapersByRepository[repository.manifestUrl].orEmpty(),
|
||||
language = language,
|
||||
isRefreshing = state.addingRepositoryUrl == repository.manifestUrl,
|
||||
onRemove = { onRemoveRepository(repository.manifestUrl) },
|
||||
onRefresh = { onRefreshRepository(repository.manifestUrl) },
|
||||
onToggleScraper = onToggleScraper,
|
||||
onOpenScraperSettings = onOpenScraperSettings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item { Spacer(Modifier.height(80.dp)) }
|
||||
}
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Text(title, color = Color.White, fontWeight = FontWeight.Black, fontSize = 22.sp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PluginsSectionHeader(title: String) {
|
||||
Text(
|
||||
title.uppercase(),
|
||||
color = Color.White.copy(alpha = 0.4f),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PluginsActionRow(label: String, onClick: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().clickable(onClick = onClick).padding(vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(label, color = Color.White)
|
||||
Text("+", color = Color.White.copy(alpha = 0.6f), fontSize = 18.sp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PluginRepositoryRow(
|
||||
repository: PluginRepositoryUiModel,
|
||||
private fun PluginRepositoryInput(
|
||||
language: String?,
|
||||
onRemove: () -> Unit
|
||||
onSubmit: (String) -> Unit
|
||||
) {
|
||||
Row(
|
||||
var url by remember { mutableStateOf("") }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 6.dp)
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.background(Color.White.copy(alpha = 0.05f))
|
||||
.padding(horizontal = 14.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.background(Color.White.copy(alpha = 0.04f))
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(repository.name.ifBlank { repository.manifestUrl }, color = Color.White, fontWeight = FontWeight.Bold)
|
||||
Text(
|
||||
AppStrings.format(language, "settings.plugins.scraper_count", repository.scraperCount),
|
||||
color = Color.White.copy(alpha = 0.5f),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
Text(
|
||||
AppStrings.t(language, "settings.plugins.remove"),
|
||||
color = FluxaColors.errorRed,
|
||||
fontSize = 13.sp,
|
||||
modifier = Modifier.clickable(onClick = onRemove).padding(8.dp)
|
||||
OutlinedTextField(
|
||||
value = url,
|
||||
onValueChange = { url = it },
|
||||
modifier = Modifier.fillMaxWidth().height(52.dp),
|
||||
singleLine = true,
|
||||
placeholder = {
|
||||
Text(
|
||||
AppStrings.t(language, "settings.plugins.repository_url_placeholder"),
|
||||
color = Color.White.copy(alpha = 0.3f),
|
||||
fontSize = 13.sp
|
||||
)
|
||||
},
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedBorderColor = Color.White.copy(alpha = 0.1f),
|
||||
focusedTextColor = Color.White,
|
||||
unfocusedTextColor = Color.White,
|
||||
cursorColor = Color.White,
|
||||
focusedContainerColor = Color.Black.copy(alpha = 0.2f),
|
||||
unfocusedContainerColor = Color.Black.copy(alpha = 0.2f)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
androidx.compose.material3.Button(
|
||||
onClick = {
|
||||
val trimmed = url.trim()
|
||||
if (trimmed.isNotEmpty()) {
|
||||
onSubmit(trimmed)
|
||||
url = ""
|
||||
}
|
||||
},
|
||||
enabled = url.isNotBlank(),
|
||||
modifier = Modifier.fillMaxWidth().height(44.dp),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
colors = androidx.compose.material3.ButtonDefaults.buttonColors(containerColor = Color.White, contentColor = Color.Black)
|
||||
) {
|
||||
Icon(Icons.Filled.Add, contentDescription = null, modifier = Modifier.size(18.dp))
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(AppStrings.t(language, "settings.plugins.add_repository"), fontWeight = FontWeight.Bold, fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PluginRepositoryGroup(
|
||||
repository: PluginRepositoryUiModel,
|
||||
scrapers: List<PluginScraperUiModel>,
|
||||
language: String?,
|
||||
isRefreshing: Boolean,
|
||||
onRemove: () -> Unit,
|
||||
onRefresh: () -> Unit,
|
||||
onToggleScraper: (String, Boolean) -> Unit,
|
||||
onOpenScraperSettings: (PluginScraperUiModel) -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.background(Color.White.copy(alpha = 0.04f))
|
||||
.border(1.dp, Color.White.copy(alpha = 0.06f), RoundedCornerShape(18.dp))
|
||||
.padding(vertical = 4.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = repository.name.ifBlank { repository.manifestUrl },
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 15.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = AppStrings.format(language, "settings.plugins.scraper_count", repository.scraperCount),
|
||||
color = Color.White.copy(alpha = 0.5f),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
if (isRefreshing) {
|
||||
Box(modifier = Modifier.size(36.dp), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator(color = Color.White.copy(alpha = 0.68f), strokeWidth = 2.dp, modifier = Modifier.size(18.dp))
|
||||
}
|
||||
} else {
|
||||
PluginIconButton(icon = Icons.Filled.Refresh, onClick = onRefresh)
|
||||
}
|
||||
PluginIconButton(icon = Icons.Filled.Close, tint = Color.White.copy(alpha = 0.5f), onClick = onRemove)
|
||||
}
|
||||
|
||||
if (scrapers.isNotEmpty()) {
|
||||
HorizontalDivider(color = Color.White.copy(alpha = 0.06f), thickness = 0.5.dp)
|
||||
scrapers.forEachIndexed { index, scraper ->
|
||||
PluginScraperRow(
|
||||
scraper = scraper,
|
||||
language = language,
|
||||
onToggle = { enabled -> onToggleScraper(scraper.id, enabled) },
|
||||
onOpenSettings = { onOpenScraperSettings(scraper) }
|
||||
)
|
||||
if (index < scrapers.lastIndex) {
|
||||
HorizontalDivider(color = Color.White.copy(alpha = 0.06f), thickness = 0.5.dp, modifier = Modifier.padding(horizontal = 16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,31 +332,31 @@ private fun PluginScraperRow(
|
|||
onOpenSettings: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(scraper.name, color = Color.White)
|
||||
Text(scraper.supportedTypes.joinToString(", "), color = Color.White.copy(alpha = 0.4f), fontSize = 11.sp)
|
||||
Text(
|
||||
text = scraper.name,
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 14.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = scraper.supportedTypes.joinToString(", "),
|
||||
color = Color.White.copy(alpha = 0.45f),
|
||||
fontSize = 11.sp
|
||||
)
|
||||
}
|
||||
if (scraper.hasSettings) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color.White.copy(alpha = 0.05f))
|
||||
.clickable(onClick = onOpenSettings),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.Settings,
|
||||
contentDescription = AppStrings.t(language, "settings.plugins.settings"),
|
||||
tint = Color.White.copy(alpha = 0.7f),
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(8.dp))
|
||||
PluginIconButton(
|
||||
icon = Icons.Filled.Settings,
|
||||
contentDescription = AppStrings.t(language, "settings.plugins.settings"),
|
||||
onClick = onOpenSettings
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
}
|
||||
Switch(
|
||||
checked = scraper.enabled,
|
||||
|
|
@ -276,47 +364,35 @@ private fun PluginScraperRow(
|
|||
colors = SwitchDefaults.colors(
|
||||
checkedTrackColor = Color.White,
|
||||
checkedThumbColor = Color.Black,
|
||||
uncheckedTrackColor = Color.White.copy(alpha = 0.15f),
|
||||
uncheckedThumbColor = Color.White.copy(alpha = 0.7f)
|
||||
uncheckedTrackColor = Color.White.copy(alpha = 0.18f),
|
||||
uncheckedThumbColor = Color.White.copy(alpha = 0.72f)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PluginAddRepositoryDialog(
|
||||
language: String?,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: (String) -> Unit
|
||||
private fun PluginIconButton(
|
||||
icon: ImageVector,
|
||||
contentDescription: String? = null,
|
||||
tint: Color = Color.White.copy(alpha = 0.68f),
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
var url by remember { mutableStateOf("") }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(AppStrings.t(language, "settings.plugins.add_repository")) },
|
||||
text = {
|
||||
TextField(
|
||||
value = url,
|
||||
onValueChange = { url = it },
|
||||
placeholder = { Text(AppStrings.t(language, "settings.plugins.repository_url_placeholder")) },
|
||||
label = { Text(AppStrings.t(language, "settings.plugins.repository_url")) },
|
||||
singleLine = true,
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.White.copy(alpha = 0.05f),
|
||||
unfocusedContainerColor = Color.White.copy(alpha = 0.05f),
|
||||
focusedTextColor = Color.White,
|
||||
unfocusedTextColor = Color.White
|
||||
)
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { onConfirm(url) }, enabled = url.isNotBlank()) {
|
||||
Text(AppStrings.t(language, "settings.plugins.add"))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(AppStrings.t(language, "settings.plugins.cancel"))
|
||||
}
|
||||
}
|
||||
)
|
||||
var focused by remember { mutableStateOf(false) }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clip(CircleShape)
|
||||
.onFocusChanged { focused = it.isFocused }
|
||||
.background(if (focused) Color.White else Color.Transparent)
|
||||
.clickable(onClick = onClick),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = if (focused) Color.Black else tint,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ class PluginsSettingsViewModel @Inject constructor(
|
|||
viewModelScope.launch { manager.removeRepository(manifestUrl) }
|
||||
}
|
||||
|
||||
fun refreshRepository(manifestUrl: String) {
|
||||
viewModelScope.launch { manager.refreshRepository(manifestUrl) }
|
||||
}
|
||||
|
||||
fun toggleScraper(scraperId: String, enabled: Boolean) {
|
||||
viewModelScope.launch { manager.toggleScraper(scraperId, enabled) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,9 +282,8 @@
|
|||
"settings.torrent_speed_default": "Default",
|
||||
"settings.torrent_speed_fast": "Fast",
|
||||
"settings.torrent_speed_ultra_fast": "Ultra Fast",
|
||||
"settings.plugins.manage": "Scraper Plugins",
|
||||
"settings.plugins.title": "Scraper Plugins",
|
||||
"settings.plugins.repositories": "Repositories",
|
||||
"settings.plugins.manage": "Plugins",
|
||||
"settings.plugins.title": "Plugins",
|
||||
"settings.plugins.add_repository": "Add Repository",
|
||||
"settings.plugins.repository_url": "Repository manifest URL",
|
||||
"settings.plugins.repository_url_placeholder": "https://example.com/repo.json",
|
||||
|
|
@ -292,11 +291,8 @@
|
|||
"settings.plugins.cancel": "Cancel",
|
||||
"settings.plugins.remove": "Remove",
|
||||
"settings.plugins.refresh": "Refresh",
|
||||
"settings.plugins.scrapers": "Scrapers",
|
||||
"settings.plugins.no_repositories": "No repositories added yet.",
|
||||
"settings.plugins.no_scrapers": "No scrapers available.",
|
||||
"settings.plugins.scraper_count": "%s scrapers",
|
||||
"settings.plugins.adding": "Adding repository…",
|
||||
"settings.plugins.error": "Failed to add repository: %s",
|
||||
"settings.plugins.settings": "Settings",
|
||||
"settings.plugins.settings_title": "%s Settings",
|
||||
|
|
@ -1128,5 +1124,7 @@
|
|||
"settings.bottom_bar_labels_desc": "Display destination names below navigation icons.",
|
||||
"settings.home_top_bar": "Home top bar",
|
||||
"settings.home_top_bar_desc": "Show Home, Series, Movies, notifications, and profile above the hero.",
|
||||
"common.add_to_library": "Add to Library"
|
||||
"common.add_to_library": "Add to Library",
|
||||
"settings.plugins.installed_repositories": "Plugin Repositories",
|
||||
"settings.plugins.adding": "Adding repository…"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,9 +282,8 @@
|
|||
"settings.torrent_speed_default": "Varsayılan",
|
||||
"settings.torrent_speed_fast": "Hızlı",
|
||||
"settings.torrent_speed_ultra_fast": "Ultra Hızlı",
|
||||
"settings.plugins.manage": "Kazıyıcı Eklentiler",
|
||||
"settings.plugins.title": "Kazıyıcı Eklentiler",
|
||||
"settings.plugins.repositories": "Depolar",
|
||||
"settings.plugins.manage": "Eklentiler",
|
||||
"settings.plugins.title": "Eklentiler",
|
||||
"settings.plugins.add_repository": "Depo Ekle",
|
||||
"settings.plugins.repository_url": "Depo manifest URL'si",
|
||||
"settings.plugins.repository_url_placeholder": "https://example.com/repo.json",
|
||||
|
|
@ -292,11 +291,8 @@
|
|||
"settings.plugins.cancel": "İptal",
|
||||
"settings.plugins.remove": "Kaldır",
|
||||
"settings.plugins.refresh": "Yenile",
|
||||
"settings.plugins.scrapers": "Kazıyıcılar",
|
||||
"settings.plugins.no_repositories": "Henüz depo eklenmedi.",
|
||||
"settings.plugins.no_scrapers": "Kullanılabilir kazıyıcı yok.",
|
||||
"settings.plugins.scraper_count": "%s kazıyıcı",
|
||||
"settings.plugins.adding": "Depo ekleniyor…",
|
||||
"settings.plugins.error": "Depo eklenemedi: %s",
|
||||
"settings.plugins.settings": "Ayarlar",
|
||||
"settings.plugins.settings_title": "%s Ayarları",
|
||||
|
|
@ -1128,5 +1124,7 @@
|
|||
"settings.bottom_bar_labels_desc": "Gezinme simgelerinin altında hedef adlarını gösterir.",
|
||||
"settings.home_top_bar": "Ana sayfa üst çubuğu",
|
||||
"settings.home_top_bar_desc": "Hero alanının üzerinde Ana Sayfa, Diziler, Filmler, bildirimler ve profili gösterir.",
|
||||
"common.add_to_library": "Kitaplığa ekle"
|
||||
"common.add_to_library": "Kitaplığa ekle",
|
||||
"settings.plugins.installed_repositories": "Eklenti Depoları",
|
||||
"settings.plugins.adding": "Depo ekleniyor…"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue