mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Redesign plugins UI and demo raw + high-level hooks
- Match exteraGram-style card layout with master toggle and plugin actions - Add usage manifest field, pin/delete/removePlugin engine APIs - Hello World plugin hooks Logger.d (raw) and send pipeline (high-level) - Bundle updated hello_world.fcplugin; stop debug auto-enable Co-authored-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
@@ -24,6 +24,7 @@ object PluginEngine : PluginHostBridge {
|
||||
val installed: StateFlow<List<PluginManifest>> = _installed.asStateFlow()
|
||||
|
||||
private val enabledIds = linkedSetOf<String>()
|
||||
private val pinnedIds = linkedSetOf<String>()
|
||||
private val loaded = mutableMapOf<String, LoadedPlugin>()
|
||||
private val settings = mutableMapOf<String, MutableMap<String, String>>()
|
||||
private val sharedUnhooks = mutableMapOf<String, MutableList<() -> Unit>>()
|
||||
@@ -72,6 +73,40 @@ object PluginEngine : PluginHostBridge {
|
||||
|
||||
fun isPluginEnabled(pluginId: String): Boolean = pluginId in enabledIds
|
||||
|
||||
fun isPluginPinned(pluginId: String): Boolean = pluginId in pinnedIds
|
||||
|
||||
fun setPluginPinned(pluginId: String, pinned: Boolean) {
|
||||
if (pinned) {
|
||||
pinnedIds += pluginId
|
||||
} else {
|
||||
pinnedIds -= pluginId
|
||||
}
|
||||
persistPinnedState()
|
||||
refreshInstalledList()
|
||||
}
|
||||
|
||||
fun sortedInstalled(): List<PluginManifest> {
|
||||
val manifests = _installed.value
|
||||
return manifests.sortedWith(
|
||||
compareByDescending<PluginManifest> { it.id in pinnedIds }
|
||||
.thenBy { it.name.lowercase() },
|
||||
)
|
||||
}
|
||||
|
||||
fun removePlugin(pluginId: String) {
|
||||
unloadPlugin(pluginId)
|
||||
enabledIds -= pluginId
|
||||
pinnedIds -= pluginId
|
||||
settings.remove(pluginId)
|
||||
val dir = pluginsRootProvider().resolve(pluginId)
|
||||
if (dir.exists()) {
|
||||
dir.deleteRecursively()
|
||||
}
|
||||
persistEnabledState()
|
||||
persistPinnedState()
|
||||
refreshInstalledList()
|
||||
}
|
||||
|
||||
fun loadedPlugin(pluginId: String): LoadedPlugin? = loaded[pluginId]
|
||||
|
||||
fun getPluginSettingBoolean(pluginId: String, key: String, default: Boolean = false): Boolean =
|
||||
@@ -238,12 +273,21 @@ object PluginEngine : PluginHostBridge {
|
||||
enabledIds.clear()
|
||||
enabledIds += enabledFile.readLines().map { it.trim() }.filter { it.isNotEmpty() }
|
||||
}
|
||||
val pinnedFile = pluginsRootProvider().resolve("pinned_plugins.txt")
|
||||
if (pinnedFile.exists()) {
|
||||
pinnedIds.clear()
|
||||
pinnedIds += pinnedFile.readLines().map { it.trim() }.filter { it.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun persistEnabledState() {
|
||||
pluginsRootProvider().resolve("enabled_plugins.txt").writeText(enabledIds.joinToString("\n"))
|
||||
}
|
||||
|
||||
private fun persistPinnedState() {
|
||||
pluginsRootProvider().resolve("pinned_plugins.txt").writeText(pinnedIds.joinToString("\n"))
|
||||
}
|
||||
|
||||
private fun projectZipExtract(archive: File, dest: File) {
|
||||
java.util.zip.ZipFile(archive).use { zip ->
|
||||
zip.entries().asSequence().forEach { entry ->
|
||||
@@ -261,10 +305,30 @@ object PluginEngine : PluginHostBridge {
|
||||
fun ensureBundledPlugins(bundledDir: File) {
|
||||
if (!bundledDir.exists()) return
|
||||
bundledDir.listFiles()?.filter { it.extension == "fcplugin" }?.forEach { archive ->
|
||||
val installed = pluginsRootProvider().resolve("hello_world")
|
||||
if (!installed.exists()) {
|
||||
val manifestInArchive = readManifestFromArchive(archive) ?: return@forEach
|
||||
val installedDir = pluginsRootProvider().resolve(manifestInArchive.id)
|
||||
val installedManifest = installedDir.resolve("manifest.json")
|
||||
val shouldInstall = when {
|
||||
!installedManifest.exists() -> true
|
||||
else -> {
|
||||
val current = runCatching {
|
||||
PluginManifestParser.parse(installedManifest.readText())
|
||||
}.getOrNull()
|
||||
current == null || current.version != manifestInArchive.version
|
||||
}
|
||||
}
|
||||
if (shouldInstall) {
|
||||
runCatching { installFcPluginArchive(archive) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readManifestFromArchive(archive: File): PluginManifest? {
|
||||
return runCatching {
|
||||
java.util.zip.ZipFile(archive).use { zip ->
|
||||
val entry = zip.getEntry("manifest.json") ?: return null
|
||||
zip.getInputStream(entry).bufferedReader().use { PluginManifestParser.parse(it.readText()) }
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ object PluginManifestParser {
|
||||
description = root["description"]?.jsonPrimitive?.contentOrNull.orEmpty(),
|
||||
author = root["author"]?.jsonPrimitive?.contentOrNull.orEmpty(),
|
||||
version = root["version"]?.jsonPrimitive?.contentOrNull ?: "1.0.0",
|
||||
usage = root["usage"]?.jsonPrimitive?.contentOrNull.orEmpty(),
|
||||
appVersion = root["app_version"]?.jsonPrimitive?.contentOrNull.orEmpty(),
|
||||
sdkVersion = root["sdk_version"]?.jsonPrimitive?.contentOrNull.orEmpty(),
|
||||
entryClass = root.requireString("entry_class"),
|
||||
|
||||
Reference in New Issue
Block a user