mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-07-27 04:12:10 +00:00
Wire ApplePluginsDataSource to a real Swift-driven state source
Adds ApplePluginsSnapshot and update() so Swift can push merged repository/scraper/settings-sheet state back into the shared Compose UI, plus FluxaApple.setPluginsActionHandler/updatePlugins following the existing setSearchHandler/updateSearch pattern. Also fixes saveScraperSettings to actually forward the settings values.
This commit is contained in:
parent
270d82d6cd
commit
c35dd1148f
2 changed files with 168 additions and 1 deletions
|
|
@ -16,7 +16,9 @@ import com.fluxa.app.shared.platform.AppleDetailSnapshot
|
|||
import com.fluxa.app.shared.platform.AppleDetailStreamSnapshot
|
||||
import com.fluxa.app.shared.platform.ApplePlaybackRequestSnapshot
|
||||
import com.fluxa.app.shared.platform.AppleAddonStoreDataSource
|
||||
import com.fluxa.app.shared.platform.ApplePluginsActionSnapshot
|
||||
import com.fluxa.app.shared.platform.ApplePluginsDataSource
|
||||
import com.fluxa.app.shared.platform.ApplePluginsSnapshot
|
||||
import com.fluxa.app.shared.platform.AppleAuthDataSource
|
||||
import com.fluxa.app.shared.platform.AppleCatalogHomeDataSource
|
||||
import com.fluxa.app.shared.platform.AppleCalendarDataSource
|
||||
|
|
@ -115,6 +117,14 @@ object FluxaApple {
|
|||
libraryDataSource.update(snapshot)
|
||||
}
|
||||
|
||||
fun setPluginsActionHandler(handler: (ApplePluginsActionSnapshot) -> Unit) {
|
||||
pluginsDataSource.setOnActionRequested(handler)
|
||||
}
|
||||
|
||||
fun updatePlugins(snapshot: ApplePluginsSnapshot) {
|
||||
pluginsDataSource.update(snapshot)
|
||||
}
|
||||
|
||||
fun parseAddonManifest(body: String): AppleAddonManifestSnapshot? = AppleStremioBridge.parseManifest(body)
|
||||
|
||||
fun normalizeAddonManifestUrl(rawUrl: String): String = AppleStremioBridge.normalizeManifestUrl(rawUrl)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,27 @@
|
|||
package com.fluxa.app.shared.platform
|
||||
|
||||
import com.fluxa.app.shared.feature.plugins.PluginRepositoryUiModel
|
||||
import com.fluxa.app.shared.feature.plugins.PluginScraperSettingsUiState
|
||||
import com.fluxa.app.shared.feature.plugins.PluginScraperUiModel
|
||||
import com.fluxa.app.shared.feature.plugins.PluginSettingsFieldUiModel
|
||||
import com.fluxa.app.shared.feature.plugins.PluginSettingsOptionUiModel
|
||||
import com.fluxa.app.shared.feature.plugins.PluginsDataSource
|
||||
import com.fluxa.app.shared.feature.plugins.PluginsUiState
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.booleanOrNull
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.doubleOrNull
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.longOrNull
|
||||
|
||||
data class ApplePluginsActionSnapshot(
|
||||
val type: String,
|
||||
|
|
@ -17,6 +34,121 @@ data class ApplePluginsActionSnapshot(
|
|||
val internalName: String? = null
|
||||
)
|
||||
|
||||
data class ApplePluginRepositorySnapshot(
|
||||
val manifestUrl: String,
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val scraperCount: Int
|
||||
)
|
||||
|
||||
data class ApplePluginSettingsOptionSnapshot(
|
||||
val label: String,
|
||||
val value: String
|
||||
)
|
||||
|
||||
data class ApplePluginSettingsFieldSnapshot(
|
||||
val key: String,
|
||||
val type: String,
|
||||
val label: String,
|
||||
val description: String?,
|
||||
val placeholder: String?,
|
||||
val isPassword: Boolean,
|
||||
val defaultValue: String?,
|
||||
val defaultBoolean: Boolean,
|
||||
val options: List<ApplePluginSettingsOptionSnapshot> = emptyList()
|
||||
)
|
||||
|
||||
data class ApplePluginScraperSnapshot(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val repositoryUrl: String,
|
||||
val enabled: Boolean,
|
||||
val supportedTypes: List<String>,
|
||||
val hasSettings: Boolean,
|
||||
val settingsJson: String
|
||||
)
|
||||
|
||||
data class ApplePluginScraperSettingsSnapshot(
|
||||
val scraper: ApplePluginScraperSnapshot,
|
||||
val loading: Boolean,
|
||||
val fields: List<ApplePluginSettingsFieldSnapshot> = emptyList()
|
||||
)
|
||||
|
||||
data class ApplePluginsSnapshot(
|
||||
val repositories: List<ApplePluginRepositorySnapshot> = emptyList(),
|
||||
val scrapers: List<ApplePluginScraperSnapshot> = emptyList(),
|
||||
val addingRepositoryUrl: String? = null,
|
||||
val repositoryError: String? = null,
|
||||
val scraperSettingsSheet: ApplePluginScraperSettingsSnapshot? = null
|
||||
)
|
||||
|
||||
private fun jsonElementToAny(element: JsonElement): Any? = when (element) {
|
||||
is JsonNull -> null
|
||||
is JsonArray -> element.map(::jsonElementToAny)
|
||||
is JsonObject -> element.mapValues { jsonElementToAny(it.value) }
|
||||
is JsonPrimitive -> when {
|
||||
element.booleanOrNull != null -> element.boolean
|
||||
element.longOrNull != null -> element.longOrNull
|
||||
element.doubleOrNull != null -> element.doubleOrNull
|
||||
else -> element.contentOrNull
|
||||
}
|
||||
}
|
||||
|
||||
private fun anyToJsonElement(value: Any?): JsonElement = when (value) {
|
||||
null -> JsonNull
|
||||
is String -> JsonPrimitive(value)
|
||||
is Boolean -> JsonPrimitive(value)
|
||||
is Number -> JsonPrimitive(value)
|
||||
else -> JsonPrimitive(value.toString())
|
||||
}
|
||||
|
||||
private fun parseSettingsJson(json: String): Map<String, Any?> =
|
||||
runCatching { Json.parseToJsonElement(json).jsonObject.mapValues { jsonElementToAny(it.value) } }
|
||||
.getOrDefault(emptyMap())
|
||||
|
||||
private fun encodeSettingsJson(values: Map<String, Any?>): String =
|
||||
Json.encodeToString(JsonObject.serializer(), JsonObject(values.mapValues(::anyToJsonElement)))
|
||||
|
||||
private fun ApplePluginRepositorySnapshot.toUiModel(): PluginRepositoryUiModel = PluginRepositoryUiModel(
|
||||
manifestUrl = manifestUrl,
|
||||
name = name,
|
||||
description = description,
|
||||
scraperCount = scraperCount
|
||||
)
|
||||
|
||||
private fun ApplePluginScraperSnapshot.toUiModel(): PluginScraperUiModel = PluginScraperUiModel(
|
||||
id = id,
|
||||
name = name,
|
||||
repositoryUrl = repositoryUrl,
|
||||
enabled = enabled,
|
||||
supportedTypes = supportedTypes,
|
||||
hasSettings = hasSettings,
|
||||
settings = parseSettingsJson(settingsJson)
|
||||
)
|
||||
|
||||
private fun ApplePluginSettingsOptionSnapshot.toUiModel(): PluginSettingsOptionUiModel = PluginSettingsOptionUiModel(
|
||||
label = label,
|
||||
value = value
|
||||
)
|
||||
|
||||
private fun ApplePluginSettingsFieldSnapshot.toUiModel(): PluginSettingsFieldUiModel = PluginSettingsFieldUiModel(
|
||||
key = key,
|
||||
type = type,
|
||||
label = label,
|
||||
description = description,
|
||||
placeholder = placeholder,
|
||||
isPassword = isPassword,
|
||||
defaultValue = defaultValue,
|
||||
defaultBoolean = defaultBoolean,
|
||||
options = options.map { it.toUiModel() }
|
||||
)
|
||||
|
||||
private fun ApplePluginScraperSettingsSnapshot.toUiState(): PluginScraperSettingsUiState = PluginScraperSettingsUiState(
|
||||
scraper = scraper.toUiModel(),
|
||||
loading = loading,
|
||||
fields = fields.map { it.toUiModel() }
|
||||
)
|
||||
|
||||
class ApplePluginsDataSource : PluginsDataSource {
|
||||
private val state = MutableStateFlow(PluginsUiState())
|
||||
private var onActionRequested: (ApplePluginsActionSnapshot) -> Unit = {}
|
||||
|
|
@ -44,6 +176,15 @@ class ApplePluginsDataSource : PluginsDataSource {
|
|||
}
|
||||
|
||||
override suspend fun requestScraperSettings(scraperId: String) {
|
||||
state.value.scrapers.find { it.id == scraperId }?.let { scraper ->
|
||||
state.value = state.value.copy(
|
||||
scraperSettingsSheet = PluginScraperSettingsUiState(
|
||||
scraper = scraper,
|
||||
loading = true,
|
||||
fields = emptyList()
|
||||
)
|
||||
)
|
||||
}
|
||||
postAction(ApplePluginsActionSnapshot(type = "requestScraperSettings", scraperId = scraperId))
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +193,13 @@ class ApplePluginsDataSource : PluginsDataSource {
|
|||
}
|
||||
|
||||
override suspend fun saveScraperSettings(scraperId: String, values: Map<String, Any?>) {
|
||||
postAction(ApplePluginsActionSnapshot(type = "saveScraperSettings", scraperId = scraperId))
|
||||
postAction(
|
||||
ApplePluginsActionSnapshot(
|
||||
type = "saveScraperSettings",
|
||||
scraperId = scraperId,
|
||||
settingsJson = encodeSettingsJson(values)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun addCloudstreamRepository(url: String) {
|
||||
|
|
@ -80,6 +227,16 @@ class ApplePluginsDataSource : PluginsDataSource {
|
|||
onActionRequested = handler
|
||||
}
|
||||
|
||||
fun update(snapshot: ApplePluginsSnapshot) {
|
||||
state.value = state.value.copy(
|
||||
repositories = snapshot.repositories.map { it.toUiModel() },
|
||||
scrapers = snapshot.scrapers.map { it.toUiModel() },
|
||||
addingRepositoryUrl = snapshot.addingRepositoryUrl,
|
||||
repositoryError = snapshot.repositoryError,
|
||||
scraperSettingsSheet = snapshot.scraperSettingsSheet?.toUiState()
|
||||
)
|
||||
}
|
||||
|
||||
private fun postAction(action: ApplePluginsActionSnapshot) {
|
||||
onActionRequested(action)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue