import { ReactNode, useCallback, useMemo, useState } from 'react'; import { createContext, useContextSelector } from 'use-context-selector'; import { getRestNonce, IS_WORDPRESS, PLUGIN_ASSISTANT_SETTINGS, PLUGIN_REST_ENDPOINTS, syncPluginAssistantSettings, } from '../constants'; export type PluginSettingsState = { allowActionPanelAiRequests: boolean; allowLogReads: boolean; allowWriteOperations: boolean; autoApproveSafeWriteOperations: boolean; requireReadApproval: boolean; setupCompleted: boolean; }; type PluginSettingsPatch = Partial; interface PluginSettingsContextProps { settings: PluginSettingsState; isSaving: boolean; error: string | null; updateSettings: (patch: PluginSettingsPatch) => Promise; } const defaultSettings: PluginSettingsState = { allowActionPanelAiRequests: Boolean( PLUGIN_ASSISTANT_SETTINGS.allowActionPanelAiRequests ?? PLUGIN_ASSISTANT_SETTINGS.assistantEnabled ), allowLogReads: Boolean(PLUGIN_ASSISTANT_SETTINGS.allowLogReads), allowWriteOperations: Boolean(PLUGIN_ASSISTANT_SETTINGS.allowWriteOperations), autoApproveSafeWriteOperations: PLUGIN_ASSISTANT_SETTINGS.autoApproveSafeWriteOperations ?? true, requireReadApproval: Boolean(PLUGIN_ASSISTANT_SETTINGS.requireReadApproval), setupCompleted: PLUGIN_ASSISTANT_SETTINGS.setupCompleted ?? true, }; const PluginSettingsContext = createContext({ settings: defaultSettings, isSaving: false, error: null, updateSettings: async () => defaultSettings, }); const coerceSettings = (value: unknown, fallback: PluginSettingsState): PluginSettingsState => { const source = value && typeof value === 'object' ? value as Record : {}; return { allowActionPanelAiRequests: Boolean( source.allowActionPanelAiRequests ?? source.assistantEnabled ?? fallback.allowActionPanelAiRequests ), allowLogReads: Boolean(source.allowLogReads ?? fallback.allowLogReads), allowWriteOperations: Boolean(source.allowWriteOperations ?? fallback.allowWriteOperations), autoApproveSafeWriteOperations: Boolean( source.autoApproveSafeWriteOperations ?? fallback.autoApproveSafeWriteOperations ), requireReadApproval: Boolean(source.requireReadApproval ?? fallback.requireReadApproval), setupCompleted: Boolean(source.setupCompleted ?? fallback.setupCompleted), }; }; export function PluginSettingsProvider({ children }: { children: ReactNode }) { const [settings, setSettings] = useState(defaultSettings); const [isSaving, setIsSaving] = useState(false); const [error, setError] = useState(null); const updateSettings = useCallback(async (patch: PluginSettingsPatch) => { if (!IS_WORDPRESS) { const nextSettings = { ...settings, ...patch }; setSettings(nextSettings); syncPluginAssistantSettings(nextSettings); return nextSettings; } setIsSaving(true); setError(null); try { const response = await fetch(PLUGIN_REST_ENDPOINTS.settings, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': getRestNonce() }, body: JSON.stringify(patch), }); const payload = await response.json().catch(() => ({})); const returnedSettings = coerceSettings(payload?.settings, settings); setSettings(returnedSettings); syncPluginAssistantSettings(returnedSettings); if (!response.ok) { throw new Error(payload?.message || 'Failed to update plugin settings.'); } return returnedSettings; } catch (e) { const message = e instanceof Error ? e.message : 'Failed to update plugin settings.'; setError(message); throw e; } finally { setIsSaving(false); } }, [settings]); const value = useMemo(() => ({ settings, isSaving, error, updateSettings, }), [settings, isSaving, error, updateSettings]); return ( {children} ); } const usePluginSettingsSelector = (selector: (state: PluginSettingsContextProps) => T): T => ( useContextSelector(PluginSettingsContext, selector) ); export const usePluginSettings = () => usePluginSettingsSelector(state => state.settings); export const usePluginSettingsSaving = () => usePluginSettingsSelector(state => state.isSaving); export const usePluginSettingsError = () => usePluginSettingsSelector(state => state.error); export const useUpdatePluginSettings = () => usePluginSettingsSelector(state => state.updateSettings);