Skip to content

Commit f8b0a17

Browse files
committed
feat(extensions): add OnInstall and OnUninstall lifecycle nodes
1 parent c47ae0c commit f8b0a17

2 files changed

Lines changed: 66 additions & 2 deletions

File tree

feature/extension/src/commonMain/kotlin/com/klyx/extension/nodegraph/MetadataNodes.kt

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.klyx.extension.nodegraph
22

33
import androidx.compose.runtime.Immutable
44
import androidx.compose.ui.graphics.Color
5+
import com.klyx.core.logging.log
56
import com.klyx.nodegraph.EvaluateScope
67
import com.klyx.nodegraph.InputPin
78
import com.klyx.nodegraph.NodeRegistry
@@ -13,6 +14,7 @@ import com.klyx.nodegraph.SequentialEventNode
1314
import com.klyx.nodegraph.customPinType
1415
import com.klyx.nodegraph.extension.GraphExtension
1516
import com.klyx.nodegraph.extension.Variable
17+
import kotlinx.coroutines.CancellationException
1618
import kotlinx.serialization.Serializable
1719

1820
internal const val EXTENSION_SCHEMA_VERSION = "1.0.0"
@@ -119,6 +121,52 @@ internal object OnInitializeMetadataNode : SequentialEventNode() {
119121
override val pins = listOf(OutputHeaderPin(defaultNextLabel))
120122
}
121123

124+
private object OnInstall : SequentialEventNode() {
125+
override val key = "klyx.extension.on_install"
126+
override val title = "On Install"
127+
override val category = "Extension"
128+
override val description = "Runs once when the extension is first installed."
129+
override val headerColor = Color(0xFF4CAF50)
130+
override val triggerName = key
131+
132+
override val pins = listOf(OutputHeaderPin(defaultNextLabel))
133+
}
134+
135+
private object OnUninstall : SequentialEventNode() {
136+
override val key = "klyx.extension.on_uninstall"
137+
override val title = "On Uninstall"
138+
override val category = "Extension"
139+
override val description = "Runs once when the extension is being uninstalled."
140+
override val headerColor = Color(0xFFD32F2F)
141+
override val triggerName = key
142+
143+
override val pins = listOf(OutputHeaderPin(defaultNextLabel))
144+
}
145+
146+
suspend fun ExtensionManager.onInstall(extensionId: String) {
147+
val graph = getById(extensionId)?.graph ?: return
148+
149+
try {
150+
graph.trigger(OnInstall.triggerName, emptyMap(), listener)
151+
} catch (e: CancellationException) {
152+
throw e
153+
} catch (e: Exception) {
154+
log.error { "Extension crash on 'OnInstall': ${e.message}" }
155+
}
156+
}
157+
158+
suspend fun ExtensionManager.onUninstall(extensionId: String) {
159+
val graph = getById(extensionId)?.graph ?: return
160+
161+
try {
162+
graph.trigger(OnUninstall.triggerName, emptyMap(), listener)
163+
} catch (e: CancellationException) {
164+
throw e
165+
} catch (e: Exception) {
166+
log.error { "Extension crash on 'OnUninstall': ${e.message}" }
167+
}
168+
}
169+
122170
object MetadataExtension : GraphExtension {
123171

124172
override val name = "klyx-extension-metadata"
@@ -144,7 +192,8 @@ object MetadataExtension : GraphExtension {
144192
registry.register(
145193
MakeMetadataNode,
146194
BreakMetadataNode,
147-
OnInitializeMetadataNode
195+
OnInitializeMetadataNode,
196+
OnInstall, OnUninstall
148197
)
149198
}
150199
}

klyx/src/commonMain/kotlin/com/klyx/ui/page/extension/ExtensionPage.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ import com.klyx.core.util.join
8585
import com.klyx.extension.nodegraph.Extension
8686
import com.klyx.extension.nodegraph.ExtensionManager
8787
import com.klyx.extension.nodegraph.ExtensionMetadata
88+
import com.klyx.extension.nodegraph.onInstall
89+
import com.klyx.extension.nodegraph.onUninstall
8890
import com.klyx.icons.Add
8991
import com.klyx.icons.Edit
9092
import com.klyx.icons.Icons
@@ -95,8 +97,10 @@ import kotlinx.coroutines.IO
9597
import kotlinx.coroutines.async
9698
import kotlinx.coroutines.coroutineScope
9799
import kotlinx.coroutines.launch
100+
import kotlinx.coroutines.withTimeoutOrNull
98101
import kotlinx.io.files.Path
99102
import kotlin.time.Clock
103+
import kotlin.time.Duration.Companion.minutes
100104

101105
@Composable
102106
context(navigationScope: NavigationScope)
@@ -305,6 +309,10 @@ fun ExtensionPage(modifier: Modifier = Modifier) {
305309
onDelete = {
306310
scope.launch(Dispatchers.IO) {
307311
try {
312+
withTimeoutOrNull(1.minutes) {
313+
extensionManager.onUninstall(metadata.id)
314+
}
315+
308316
extensionManager.removeExtension(extension)
309317
snackbarHostState.showSnackbar("${metadata.name} deleted.")
310318
} catch (_: Exception) {
@@ -354,7 +362,7 @@ fun ExtensionPage(modifier: Modifier = Modifier) {
354362
val localExt = extensions.find { it.metadata.id == storeExt.id }
355363
val isInstalled = localExt != null
356364

357-
val hasUpdate = isInstalled && localExt!!.metadata.version != storeExt.version
365+
val hasUpdate = isInstalled && localExt.metadata.version != storeExt.version
358366

359367
StoreExtensionItem(
360368
storeExt = storeExt,
@@ -365,6 +373,7 @@ fun ExtensionPage(modifier: Modifier = Modifier) {
365373
onInstall = {
366374
val destDir = Paths.extensionsDir
367375
val installedPath = store.downloadExtension(storeExt, destDir)
376+
368377
if (installedPath != null) {
369378
extensionManager.addOrReplaceExtension(installedPath, isLocal = false)
370379
snackbarHostState.showSnackbar("${storeExt.name} Installed!")
@@ -374,6 +383,12 @@ fun ExtensionPage(modifier: Modifier = Modifier) {
374383
storeExtensions[index] =
375384
storeExt.copy(downloadCount = storeExt.downloadCount + 1)
376385
}
386+
387+
scope.launch(Dispatchers.IO) {
388+
withTimeoutOrNull(5.minutes) {
389+
extensionManager.onInstall(storeExt.id)
390+
}
391+
}
377392
} else {
378393
snackbarHostState.showSnackbar("Failed to install ${storeExt.name}.")
379394
}

0 commit comments

Comments
 (0)