import React, { useCallback, useEffect, useState } from 'react' import { __ } from '@wordpress/i18n' import { useRestAPI } from '../../../hooks/useRestAPI' import { useSelection } from '../../../hooks/useSelection' import { createContextHook } from '../../../utils/bootstrap' import { REST_BASES } from '../../../utils/restAPI' import { isNetworkAdmin } from '../../../utils/screen' import { useMigrationOptions } from './WithMigrationOptions' import type { RestAPI } from '../../../hooks/useRestAPI' import type { UseSelection } from '../../../hooks/useSelection' import type { PropsWithChildren } from 'react' export enum MigrationStep { LoadImporters, FetchSnippets, MigrateSnippets } export interface MigrationError { step: MigrationStep message: string } export interface Importer { name: string title: string is_active: boolean } export interface ImportableSnippet { id: number title: string table_data: { id: number title: string } } export interface ImportRequest { ids: number[] network?: boolean auto_add_tags?: boolean tag_value?: string } export interface ImportResponse { imported: number[] } const makeMigrationRequest = async (api: RestAPI, importer: string, request: ImportRequest): Promise => { if (0 === request.ids.length) { throw new Error(__('Please select snippets to import.', 'code-snippets')) } if (!importer) { throw new Error(__('Please select an importer.', 'code-snippets')) } const response = await api.post(`${REST_BASES.import.plugins}/${importer}/import`, request) if (1 > response.imported.length) { throw new Error(__('No snippets were imported.', 'code-snippets')) } return response.imported } const useRemoteRequest = ( step: MigrationStep, setError: (error: MigrationError | undefined) => void, setIsWorking: (step: MigrationStep | undefined) => void ): [T[], (makeRequest: () => Promise, onSuccess?: VoidFunction) => void, VoidFunction] => { const [data, setData] = useState([]) const clearData = useCallback(() => setData([]), []) const requestData = useCallback((makeRequest: () => Promise, onSuccess?: VoidFunction) => { setError(undefined) setIsWorking(step) setData([]) makeRequest() .then(response => { setData(response) onSuccess?.() }) .catch((error: unknown) => { setError({ step, message: error instanceof Error ? error.message : 'Unknown error' }) }) .finally(() => setIsWorking(undefined)) }, [setError, setIsWorking, step]) return [data, requestData, clearData] } export interface MigrationDataContext { error: MigrationError | undefined importSnippets: VoidFunction importers: Importer[] isWorking: MigrationStep | undefined changeSelectedImporter: (newImporter: string) => void snippetSelection: UseSelection importedIds: number[] } const [Context, useMigrationData] = createContextHook('useMigrationData') export const WithMigrationData: React.FC = ({ children }) => { const { api } = useRestAPI() const { selectedImporter, setSelectedImporter, autoAddTags, tagValue } = useMigrationOptions() const [error, setError] = useState() const [isWorking, setIsWorking] = useState() const [importedIds, doImport] = useRemoteRequest(MigrationStep.MigrateSnippets, setError, setIsWorking) const [importers, fetchImporters] = useRemoteRequest(MigrationStep.LoadImporters, setError, setIsWorking) const [snippets, fetchSnippets, resetSnippets] = useRemoteRequest(MigrationStep.FetchSnippets, setError, setIsWorking) const snippetSelection = useSelection(snippets, snippet => snippet.table_data.id) useEffect(() => { fetchImporters(() => api .get>(REST_BASES.import.plugins) .then(response => Object.values(response))) }, [api, fetchImporters]) useEffect(() => { if (selectedImporter) { fetchSnippets(() => api.get(`${REST_BASES.import.plugins}/${selectedImporter}`)) } }, [api, selectedImporter, fetchSnippets]) const importSnippets = () => { const request: ImportRequest = { ids: Array.from(snippetSelection.selectedItems), network: isNetworkAdmin(), auto_add_tags: autoAddTags, tag_value: autoAddTags ? tagValue : undefined } const handleImportSuccess = () => { resetSnippets() snippetSelection.clearSelection() } doImport(() => makeMigrationRequest(api, selectedImporter, request), handleImportSuccess) } const changeSelectedImporter = (newImporter: string) => { setSelectedImporter(newImporter) snippetSelection.clearSelection() setError(undefined) resetSnippets() } const value: MigrationDataContext = { error, snippetSelection, importSnippets, importers, isWorking, changeSelectedImporter, importedIds } return {children} } export { useMigrationData }