Barazo default frontend barazo.forum
at main 101 lines 3.1 kB view raw
1/** 2 * Plugin context provider. 3 * Fetches enabled plugins and their settings from the API on mount. 4 * Exposes helpers to check plugin state and a refresh method for admin pages. 5 */ 6 7'use client' 8 9import { createContext, useCallback, useEffect, useMemo, useState } from 'react' 10import type { ReactNode } from 'react' 11import type { Plugin } from '@/lib/api/types' 12import { getPlugins } from '@/lib/api/client' 13import { useAuth } from '@/hooks/use-auth' 14import { loadBundledPlugins } from '@/lib/plugins/loader' 15 16export interface PluginContextValue { 17 /** List of all plugins (enabled and disabled) */ 18 plugins: Plugin[] 19 /** Check whether a plugin with the given name is enabled */ 20 isPluginEnabled: (name: string) => boolean 21 /** Get the settings for a plugin by name, or null if not found */ 22 getPluginSettings: (name: string) => Record<string, unknown> | null 23 /** Whether the plugin list is still loading */ 24 isLoading: boolean 25 /** Re-fetch the plugin list (call after admin changes) */ 26 refreshPlugins: () => Promise<void> 27} 28 29export const PluginContext = createContext<PluginContextValue | null>(null) 30 31interface PluginProviderProps { 32 children: ReactNode 33} 34 35export function PluginProvider({ children }: PluginProviderProps) { 36 const { getAccessToken, isLoading: authLoading } = useAuth() 37 const [plugins, setPlugins] = useState<Plugin[]>([]) 38 const [isLoading, setIsLoading] = useState(true) 39 40 const fetchPlugins = useCallback(async () => { 41 const token = getAccessToken() 42 if (!token) { 43 // Unauthenticated: no plugin data available from API 44 setPlugins([]) 45 setIsLoading(false) 46 return 47 } 48 49 try { 50 const response = await getPlugins(token) 51 const enabledNames = response.plugins.filter((p) => p.enabled).map((p) => p.name) 52 await loadBundledPlugins(enabledNames) 53 setPlugins(response.plugins) 54 } catch { 55 // On error, keep existing plugins (or empty on first load) 56 setPlugins((prev) => prev) 57 } finally { 58 setIsLoading(false) 59 } 60 }, [getAccessToken]) 61 62 const refreshPlugins = useCallback(async () => { 63 setIsLoading(true) 64 await fetchPlugins() 65 }, [fetchPlugins]) 66 67 useEffect(() => { 68 if (authLoading) return 69 void fetchPlugins() 70 }, [authLoading, fetchPlugins]) 71 72 const isPluginEnabled = useCallback( 73 (name: string): boolean => { 74 const plugin = plugins.find((p) => p.name === name) 75 return plugin?.enabled ?? false 76 }, 77 [plugins] 78 ) 79 80 const getPluginSettings = useCallback( 81 (name: string): Record<string, unknown> | null => { 82 const plugin = plugins.find((p) => p.name === name) 83 if (!plugin) return null 84 return { ...plugin.settings } 85 }, 86 [plugins] 87 ) 88 89 const value = useMemo<PluginContextValue>( 90 () => ({ 91 plugins, 92 isPluginEnabled, 93 getPluginSettings, 94 isLoading, 95 refreshPlugins, 96 }), 97 [plugins, isPluginEnabled, getPluginSettings, isLoading, refreshPlugins] 98 ) 99 100 return <PluginContext.Provider value={value}>{children}</PluginContext.Provider> 101}