mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-17 08:15:44 +00:00
Implement plugin enabling & disabling
This commit is contained in:
@@ -14,6 +14,10 @@ export default function PluginManagement() {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
function updatePlugin(plugin: PluginDto) {
|
||||||
|
setPlugins(plugins.map(p => p.id === plugin.id ? plugin : p));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row flex-grow justify-between mb-8">
|
<div className="flex flex-row flex-grow justify-between mb-8">
|
||||||
@@ -26,7 +30,10 @@ export default function PluginManagement() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-300px gap-4">
|
<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>
|
||||||
|
|
||||||
<div className="flex flex-row flex-grow justify-between my-8">
|
<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 PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
|
||||||
import PluginConfigElement from "Frontend/generated/de/grimsi/gameyfin/pluginapi/core/PluginConfigElement";
|
import PluginConfigElement from "Frontend/generated/de/grimsi/gameyfin/pluginapi/core/PluginConfigElement";
|
||||||
import Input from "Frontend/components/general/Input";
|
import Input from "Frontend/components/general/Input";
|
||||||
import {PuzzlePiece} from "@phosphor-icons/react";
|
import {Plug} from "@phosphor-icons/react";
|
||||||
|
|
||||||
interface PluginDetailsModalProps {
|
interface PluginDetailsModalProps {
|
||||||
plugin: PluginDto;
|
plugin: PluginDto;
|
||||||
@@ -52,7 +52,7 @@ export default function PluginDetailsModal({plugin, isOpen, onOpenChange}: Plugi
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<h4 className="text-l font-bold">Details</h4>
|
<h4 className="text-l font-bold">Details</h4>
|
||||||
<div className="flex flex-row gap-8">
|
<div className="flex flex-row gap-8">
|
||||||
<PuzzlePiece size={64} weight="fill"/>
|
<Plug size={64} weight="fill"/>
|
||||||
<div className="grid grid-cols-2">
|
<div className="grid grid-cols-2">
|
||||||
<p>Author: {plugin.author}</p>
|
<p>Author: {plugin.author}</p>
|
||||||
<p>Version: {plugin.version}</p>
|
<p>Version: {plugin.version}</p>
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import {Card, Chip, Skeleton, useDisclosure} from "@nextui-org/react";
|
import {Button, Card, Chip, Skeleton, Tooltip, useDisclosure} from "@nextui-org/react";
|
||||||
import {PuzzlePiece} from "@phosphor-icons/react";
|
import {Plug, Power, SlidersHorizontal} from "@phosphor-icons/react";
|
||||||
import {PluginManagementEndpoint} from "Frontend/generated/endpoints";
|
import {PluginManagementEndpoint} from "Frontend/generated/endpoints";
|
||||||
import PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
|
import PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
|
||||||
import PluginState from "Frontend/generated/org/pf4j/PluginState";
|
import PluginState from "Frontend/generated/org/pf4j/PluginState";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import PluginDetailsModal from "Frontend/components/general/PluginDetailsModal";
|
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 pluginDetailsModal = useDisclosure();
|
||||||
const [configValid, setConfigValid] = useState<boolean | undefined>(undefined);
|
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" {
|
function borderColor(state: PluginState | undefined): "success" | "warning" | "danger" | "default" {
|
||||||
|
if (isDisabled(state)) return "warning";
|
||||||
if (configValid === undefined) return "default";
|
if (configValid === undefined) return "default";
|
||||||
if (!configValid) return "danger";
|
if (!configValid) return "danger";
|
||||||
return stateToColor(state);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card className={`flex flex-row justify-between p-2 border-2 border-${borderColor(plugin.state)}`}
|
<Card className={`flex flex-row justify-between p-2 border-2 border-${borderColor(plugin.state)}`}>
|
||||||
isPressable={true} onPress={pluginDetailsModal.onOpen}>
|
<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">
|
<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>
|
<p className="font-semibold">{plugin.name}</p>
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<Chip size="sm" radius="sm" className="text-xs">{plugin.version}</Chip>
|
<Chip size="sm" radius="sm" className="text-xs">{plugin.version}</Chip>
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ class DatabasePluginStatusProvider(
|
|||||||
override fun disablePlugin(pluginId: String) {
|
override fun disablePlugin(pluginId: String) {
|
||||||
val pluginManagement = pluginManagementRepository.findByIdOrNull(pluginId)
|
val pluginManagement = pluginManagementRepository.findByIdOrNull(pluginId)
|
||||||
if (pluginManagement != null) {
|
if (pluginManagement != null) {
|
||||||
pluginManagement.enabled = true
|
pluginManagement.enabled = false
|
||||||
pluginManagementRepository.save(pluginManagement)
|
pluginManagementRepository.save(pluginManagement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ class DatabasePluginStatusProvider(
|
|||||||
override fun enablePlugin(pluginId: String) {
|
override fun enablePlugin(pluginId: String) {
|
||||||
val pluginManagement = pluginManagementRepository.findByIdOrNull(pluginId)
|
val pluginManagement = pluginManagementRepository.findByIdOrNull(pluginId)
|
||||||
if (pluginManagement != null) {
|
if (pluginManagement != null) {
|
||||||
pluginManagement.enabled = false
|
pluginManagement.enabled = true
|
||||||
pluginManagementRepository.save(pluginManagement)
|
pluginManagementRepository.save(pluginManagement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-1
@@ -22,7 +22,18 @@ class GameyfinPluginManager(
|
|||||||
// This took me way too long to figure out...
|
// This took me way too long to figure out...
|
||||||
// But I learned a lot about Kotlin and Java interoperability in the process
|
// But I learned a lot about Kotlin and Java interoperability in the process
|
||||||
init {
|
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 {
|
override fun createPluginLoader(): PluginLoader {
|
||||||
|
|||||||
+2
@@ -11,6 +11,8 @@ class PluginManagementEndpoint(
|
|||||||
) {
|
) {
|
||||||
fun getPlugins() = pluginManagementService.getPlugins()
|
fun getPlugins() = pluginManagementService.getPlugins()
|
||||||
|
|
||||||
|
fun getPlugin(pluginId: String) = pluginManagementService.getPlugin(pluginId)
|
||||||
|
|
||||||
fun startPlugin(pluginId: String) = pluginManagementService.startPlugin(pluginId)
|
fun startPlugin(pluginId: String) = pluginManagementService.startPlugin(pluginId)
|
||||||
|
|
||||||
fun stopPlugin(pluginId: String) = pluginManagementService.stopPlugin(pluginId)
|
fun stopPlugin(pluginId: String) = pluginManagementService.stopPlugin(pluginId)
|
||||||
|
|||||||
+11
@@ -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) {
|
fun startPlugin(pluginId: String) {
|
||||||
pluginManager.startPlugin(pluginId)
|
pluginManager.startPlugin(pluginId)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user