import { AxiosError, isAxiosError } from 'axios' import React, { createContext, Dispatch, PropsWithChildren, SetStateAction, useCallback, useContext, useMemo, useState } from 'react' import { Notice } from '../../types/Notice' import { Snippet } from '../../types/Snippet' import { CodeEditorInstance } from '../../types/WordPressCodeEditor' import { isLicensed, isProSnippet } from '../../utils/snippets' import { useSnippetSubmit } from '../utils/submit' export interface SnippetFormContext { snippet: Snippet setSnippet: Dispatch> updateSnippet: Dispatch> isReadOnly: boolean isWorking: boolean setIsWorking: Dispatch> currentNotice: Notice | undefined setCurrentNotice: Dispatch> codeEditorInstance: CodeEditorInstance | undefined setCodeEditorInstance: Dispatch> handleRequestError: (error: AxiosError | unknown, message?: string) => void submitSnippet: VoidFunction submitAndActivateSnippet: VoidFunction submitAndDeactivateSnippet: VoidFunction } const SnippetFormContext = createContext(undefined) export const useSnippetForm = () => { const value = useContext(SnippetFormContext) if (value === undefined) { throw Error('useSnippetForm can only be used within a SnippetForm context provider') } return value } export interface WithSnippetFormContextProps extends PropsWithChildren { initialSnippet: () => Snippet } export const WithSnippetFormContext: React.FC = ({ children, initialSnippet }) => { const [snippet, setSnippet] = useState(initialSnippet) const [isWorking, setIsWorking] = useState(false) const [currentNotice, setCurrentNotice] = useState() const [codeEditorInstance, setCodeEditorInstance] = useState() const submitSnippet = useSnippetSubmit(setSnippet, setIsWorking, setCurrentNotice) const isReadOnly = useMemo(() => !isLicensed() && isProSnippet(snippet.scope), [snippet.scope]) const handleRequestError = useCallback((error: AxiosError | unknown, message?: string) => { console.error('Request failed', error) setIsWorking(false) setCurrentNotice(['error', [message, isAxiosError(error) ? error.message : ''].filter(Boolean).join(' ')]) }, [setIsWorking, setCurrentNotice]) const updateSnippet: Dispatch> = useCallback((value: SetStateAction) => { setSnippet(previous => { const updated = 'object' === typeof value ? value : value(previous) codeEditorInstance?.codemirror.setValue(updated.code) window.tinymce?.activeEditor.setContent(updated.desc) return updated }) }, [codeEditorInstance?.codemirror]) const value: SnippetFormContext = { snippet, setSnippet, updateSnippet, isReadOnly, isWorking, setIsWorking, currentNotice, setCurrentNotice, codeEditorInstance, setCodeEditorInstance, handleRequestError, submitSnippet: () => submitSnippet(snippet), submitAndActivateSnippet: () => submitSnippet(snippet, true), submitAndDeactivateSnippet: () => submitSnippet(snippet, false) } return {children} }