import { isAxiosError } from 'axios' import React, { useCallback, useMemo, useState } from 'react' import { createContextHook } from '../../../utils/bootstrap' import { isLicensed } from '../../../utils/screen' import { isProSnippet } from '../../../utils/snippets/snippets' import type { Dispatch, PropsWithChildren, SetStateAction } from 'react' import type { ScreenNotice } from '../../../types/ScreenNotice' import type { Snippet } from '../../../types/Snippet' import type { CodeEditorInstance } from '../../../types/vendor/WordPressCodeEditor' export interface SnippetFormContext { snippet: Snippet isWorking: boolean isReadOnly: boolean isDirty: boolean setSnippet: Dispatch> acceptSnippet: (snippet: Snippet) => void updateSnippet: Dispatch> setIsWorking: Dispatch> currentNotice: ScreenNotice | undefined setCurrentNotice: Dispatch> codeEditorInstance: CodeEditorInstance | undefined handleRequestError: (error: unknown, message?: string) => void setCodeEditorInstance: Dispatch> } const [Context, useSnippetForm] = createContextHook('useSnippetForm') export interface WithSnippetFormContextProps extends PropsWithChildren { initialSnippet: () => Snippet } const getSnippetDraftState = (snippet: Snippet) => ({ name: snippet.name, desc: snippet.desc, code: snippet.code, tags: snippet.tags, scope: snippet.scope, priority: snippet.priority, active: snippet.active, locked: snippet.locked, network: snippet.network, sharedNetwork: snippet.shared_network, conditionId: snippet.conditionId }) const isSnippetDraftDirty = (snippet: Snippet, savedSnippet: Snippet): boolean => { const draftState = JSON.stringify(getSnippetDraftState(snippet)) const savedDraftState = JSON.stringify(getSnippetDraftState(savedSnippet)) return draftState !== savedDraftState } export const WithSnippetFormContext: React.FC = ({ children, initialSnippet }) => { const [initialValue] = useState(initialSnippet) const [snippet, setSnippet] = useState(initialValue) const [savedSnippet, setSavedSnippet] = useState(initialValue) const [isWorking, setIsWorking] = useState(false) const [currentNotice, setCurrentNotice] = useState() const [codeEditorInstance, setCodeEditorInstance] = useState() const isReadOnly = useMemo( () => snippet.locked || !isLicensed() && isProSnippet({ scope: snippet.scope }), [snippet.locked, snippet.scope] ) const isDirty = useMemo( () => isSnippetDraftDirty(snippet, savedSnippet), [snippet, savedSnippet] ) const acceptSnippet = useCallback((value: Snippet) => { setSnippet(value) setSavedSnippet(value) }, []) const handleRequestError = useCallback((error: 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, isWorking, isReadOnly, isDirty, setSnippet, acceptSnippet, setIsWorking, updateSnippet, currentNotice, setCurrentNotice, codeEditorInstance, handleRequestError, setCodeEditorInstance } return {children} } export { useSnippetForm }