| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { useState, useEffect, useRef } from "react"; |
| import { api } from "@/lib/api"; |
| import type { PluginManifest, RegisteredPlugin } from "./types"; |
| import { getPluginComponent, onPluginRegistered } from "./registry"; |
|
|
| export function usePlugins() { |
| const [manifests, setManifests] = useState<PluginManifest[]>([]); |
| const [plugins, setPlugins] = useState<RegisteredPlugin[]>([]); |
| const [loading, setLoading] = useState(true); |
| const loadedScripts = useRef<Set<string>>(new Set()); |
|
|
| |
| useEffect(() => { |
| api |
| .getPlugins() |
| .then((list) => { |
| setManifests(list); |
| if (list.length === 0) setLoading(false); |
| }) |
| .catch(() => setLoading(false)); |
| }, []); |
|
|
| |
| useEffect(() => { |
| if (manifests.length === 0) return; |
|
|
| for (const manifest of manifests) { |
| |
| if (manifest.css) { |
| const cssUrl = `/dashboard-plugins/${manifest.name}/${manifest.css}`; |
| if (!document.querySelector(`link[href="${cssUrl}"]`)) { |
| const link = document.createElement("link"); |
| link.rel = "stylesheet"; |
| link.href = cssUrl; |
| document.head.appendChild(link); |
| } |
| } |
|
|
| |
| const jsUrl = `/dashboard-plugins/${manifest.name}/${manifest.entry}`; |
| if (loadedScripts.current.has(jsUrl)) continue; |
| loadedScripts.current.add(jsUrl); |
|
|
| const script = document.createElement("script"); |
| script.src = jsUrl; |
| script.async = true; |
| script.onerror = () => { |
| console.warn(`[plugins] Failed to load ${manifest.name} from ${jsUrl}`); |
| }; |
| document.body.appendChild(script); |
| } |
|
|
| |
| const timeout = setTimeout(() => setLoading(false), 2000); |
| return () => clearTimeout(timeout); |
| }, [manifests]); |
|
|
| |
| useEffect(() => { |
| function resolvePlugins() { |
| const resolved: RegisteredPlugin[] = []; |
| for (const manifest of manifests) { |
| const component = getPluginComponent(manifest.name); |
| if (component) { |
| resolved.push({ manifest, component }); |
| } |
| } |
| setPlugins(resolved); |
| |
| if (resolved.length === manifests.length && manifests.length > 0) { |
| setLoading(false); |
| } |
| } |
|
|
| resolvePlugins(); |
| const unsub = onPluginRegistered(resolvePlugins); |
| return unsub; |
| }, [manifests]); |
|
|
| return { plugins, manifests, loading }; |
| } |
|
|