Implement plugin enabling & disabling

This commit is contained in:
grimsi
2024-12-19 16:34:01 +01:00
parent 798328a292
commit c365574a92
7 changed files with 85 additions and 12 deletions
@@ -14,6 +14,10 @@ export default function PluginManagement() {
});
}, []);
function updatePlugin(plugin: PluginDto) {
setPlugins(plugins.map(p => p.id === plugin.id ? plugin : p));
}
return (
<div className="flex flex-col">
<div className="flex flex-row flex-grow justify-between mb-8">
@@ -26,7 +30,10 @@ export default function PluginManagement() {
</div>
<div className="grid grid-cols-300px gap-4">
{plugins.map((plugin) => <PluginManagementCard plugin={plugin} key={plugin.name}/>)}
{plugins.map((plugin) => <PluginManagementCard plugin={plugin}
updatePlugin={updatePlugin}
key={plugin.name}/>
)}
</div>
<div className="flex flex-row flex-grow justify-between my-8">
@@ -6,7 +6,7 @@ import {PluginConfigEndpoint} from "Frontend/generated/endpoints";
import PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
import PluginConfigElement from "Frontend/generated/de/grimsi/gameyfin/pluginapi/core/PluginConfigElement";
import Input from "Frontend/components/general/Input";
import {PuzzlePiece} from "@phosphor-icons/react";
import {Plug} from "@phosphor-icons/react";
interface PluginDetailsModalProps {
plugin: PluginDto;
@@ -52,7 +52,7 @@ export default function PluginDetailsModal({plugin, isOpen, onOpenChange}: Plugi
<ModalBody>
<h4 className="text-l font-bold">Details</h4>
<div className="flex flex-row gap-8">
<PuzzlePiece size={64} weight="fill"/>
<Plug size={64} weight="fill"/>
<div className="grid grid-cols-2">
<p>Author: {plugin.author}</p>
<p>Version: {plugin.version}</p>
@@ -1,12 +1,15 @@
import {Card, Chip, Skeleton, useDisclosure} from "@nextui-org/react";
import {PuzzlePiece} from "@phosphor-icons/react";
import {Button, Card, Chip, Skeleton, Tooltip, useDisclosure} from "@nextui-org/react";
import {Plug, Power, SlidersHorizontal} from "@phosphor-icons/react";
import {PluginManagementEndpoint} from "Frontend/generated/endpoints";
import PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
import PluginState from "Frontend/generated/org/pf4j/PluginState";
import React, {useEffect, useState} from "react";
import PluginDetailsModal from "Frontend/components/general/PluginDetailsModal";
export function PluginManagementCard({plugin}: { plugin: PluginDto }) {
export function PluginManagementCard({plugin, updatePlugin}: {
plugin: PluginDto,
updatePlugin: (plugin: PluginDto) => void
}) {
const pluginDetailsModal = useDisclosure();
const [configValid, setConfigValid] = useState<boolean | undefined>(undefined);
@@ -17,7 +20,12 @@ export function PluginManagementCard({plugin}: { plugin: PluginDto }) {
});
}, []);
function iconColor(state: PluginState | undefined): "white" | "default" {
return "default";
}
function borderColor(state: PluginState | undefined): "success" | "warning" | "danger" | "default" {
if (isDisabled(state)) return "warning";
if (configValid === undefined) return "default";
if (!configValid) return "danger";
return stateToColor(state);
@@ -36,12 +44,46 @@ export function PluginManagementCard({plugin}: { plugin: PluginDto }) {
}
}
function isDisabled(state: PluginState | undefined): boolean {
return state === PluginState.DISABLED;
}
function togglePluginEnabled() {
if (isDisabled(plugin.state)) {
PluginManagementEndpoint.enablePlugin(plugin.id).then(() => {
PluginManagementEndpoint.getPlugin(plugin.id).then((response) => {
if (response === undefined) return;
updatePlugin(response);
});
});
} else {
PluginManagementEndpoint.disablePlugin(plugin.id).then(() => {
PluginManagementEndpoint.getPlugin(plugin.id).then((response) => {
if (response === undefined) return;
updatePlugin(response);
});
});
}
}
return (
<>
<Card className={`flex flex-row justify-between p-2 border-2 border-${borderColor(plugin.state)}`}
isPressable={true} onPress={pluginDetailsModal.onOpen}>
<Card className={`flex flex-row justify-between p-2 border-2 border-${borderColor(plugin.state)}`}>
<div className="absolute right-0 top-0 flex flex-row">
<Tooltip content={`${isDisabled(plugin.state) ? "Enable" : "Disable"} plugin`} placement="bottom"
color="foreground">
<Button isIconOnly variant="ghost" onPress={() => togglePluginEnabled()}>
<Power/>
</Button>
</Tooltip>
<Tooltip content="Configuration" placement="bottom" color="foreground">
<Button isIconOnly variant="ghost" onPress={pluginDetailsModal.onOpen}>
<SlidersHorizontal/>
</Button>
</Tooltip>
</div>
<div className="flex flex-1 flex-col items-center gap-1">
<PuzzlePiece size={64} weight="fill"/>
<Plug size={64} weight="fill"/>
<p className="font-semibold">{plugin.name}</p>
<div className="flex flex-row gap-2">
<Chip size="sm" radius="sm" className="text-xs">{plugin.version}</Chip>
@@ -17,7 +17,7 @@ class DatabasePluginStatusProvider(
override fun disablePlugin(pluginId: String) {
val pluginManagement = pluginManagementRepository.findByIdOrNull(pluginId)
if (pluginManagement != null) {
pluginManagement.enabled = true
pluginManagement.enabled = false
pluginManagementRepository.save(pluginManagement)
}
}
@@ -25,7 +25,7 @@ class DatabasePluginStatusProvider(
override fun enablePlugin(pluginId: String) {
val pluginManagement = pluginManagementRepository.findByIdOrNull(pluginId)
if (pluginManagement != null) {
pluginManagement.enabled = false
pluginManagement.enabled = true
pluginManagementRepository.save(pluginManagement)
}
}
@@ -22,7 +22,18 @@ class GameyfinPluginManager(
// This took me way too long to figure out...
// But I learned a lot about Kotlin and Java interoperability in the process
init {
this.pluginStatusProvider = dbPluginStatusProvider
pluginStatusProvider = dbPluginStatusProvider
pluginStateListeners.add { event ->
if (event is PluginStateEvent) {
log.info { "Plugin ${event.plugin.pluginId} changed state to ${event.pluginState}" }
if (event.oldState == PluginState.DISABLED) {
startPlugin(event.plugin.pluginId)
} else if (event.pluginState == PluginState.DISABLED) {
stopPlugin(event.plugin.pluginId)
}
}
}
}
override fun createPluginLoader(): PluginLoader {
@@ -11,6 +11,8 @@ class PluginManagementEndpoint(
) {
fun getPlugins() = pluginManagementService.getPlugins()
fun getPlugin(pluginId: String) = pluginManagementService.getPlugin(pluginId)
fun startPlugin(pluginId: String) = pluginManagementService.startPlugin(pluginId)
fun stopPlugin(pluginId: String) = pluginManagementService.stopPlugin(pluginId)
@@ -18,6 +18,17 @@ class PluginManagementService(
}
}
fun getPlugin(pluginId: String): PluginDto {
val plugin = pluginManager.getPlugin(pluginId)
return PluginDto(
plugin.pluginId,
plugin.descriptor.pluginDescription,
plugin.descriptor.version,
plugin.descriptor.provider,
plugin.pluginState
)
}
fun startPlugin(pluginId: String) {
pluginManager.startPlugin(pluginId)
}