PluginProbe
Code Snippets / 4.0.0-beta.2
Code Snippets v4.0.0-beta.2
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / components / ImportMenu / MigrateForm / WithMigrationData.tsx

WithMigrationData.tsx in Code Snippets 4.0.0-beta.2, at js/components/ImportMenu/MigrateForm/WithMigrationData.tsx

171 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React, { useCallback, useEffect, useState } from 'react'
2 import { __ } from '@wordpress/i18n'
3 import { useRestAPI } from '../../../hooks/useRestAPI'
4 import { useSelection } from '../../../hooks/useSelection'
5 import { createContextHook } from '../../../utils/bootstrap'
6 import { REST_BASES } from '../../../utils/restAPI'
7 import { isNetworkAdmin } from '../../../utils/screen'
8 import { useMigrationOptions } from './WithMigrationOptions'
9 import type { RestAPI } from '../../../hooks/useRestAPI'
10 import type { UseSelection } from '../../../hooks/useSelection'
11 import type { PropsWithChildren } from 'react'
12
13 export enum MigrationStep {
14 LoadImporters,
15 FetchSnippets,
16 MigrateSnippets
17 }
18
19 export interface MigrationError {
20 step: MigrationStep
21 message: string
22 }
23
24 export interface Importer {
25 name: string
26 title: string
27 is_active: boolean
28 }
29
30 export interface ImportableSnippet {
31 id: number
32 title: string
33 table_data: {
34 id: number
35 title: string
36 }
37 }
38
39 export interface ImportRequest {
40 ids: number[]
41 network?: boolean
42 auto_add_tags?: boolean
43 tag_value?: string
44 }
45
46 export interface ImportResponse {
47 imported: number[]
48 }
49
50 const makeMigrationRequest = async (api: RestAPI, importer: string, request: ImportRequest): Promise<number[]> => {
51 if (0 === request.ids.length) {
52 throw new Error(__('Please select snippets to import.', 'code-snippets'))
53 }
54
55 if (!importer) {
56 throw new Error(__('Please select an importer.', 'code-snippets'))
57 }
58
59 const response = await api.post<ImportResponse, ImportRequest>(`${REST_BASES.import.plugins}/${importer}/import`, request)
60
61 if (1 > response.imported.length) {
62 throw new Error(__('No snippets were imported.', 'code-snippets'))
63 }
64
65 return response.imported
66 }
67
68 const useRemoteRequest = <T, >(
69 step: MigrationStep,
70 setError: (error: MigrationError | undefined) => void,
71 setIsWorking: (step: MigrationStep | undefined) => void
72 ): [T[], (makeRequest: () => Promise<T[]>, onSuccess?: VoidFunction) => void, VoidFunction] => {
73 const [data, setData] = useState<T[]>([])
74
75 const clearData = useCallback(() => setData([]), [])
76
77 const requestData = useCallback((makeRequest: () => Promise<T[]>, onSuccess?: VoidFunction) => {
78 setError(undefined)
79 setIsWorking(step)
80 setData([])
81
82 makeRequest()
83 .then(response => {
84 setData(response)
85 onSuccess?.()
86 })
87 .catch((error: unknown) => {
88 setError({ step, message: error instanceof Error ? error.message : 'Unknown error' })
89 })
90 .finally(() => setIsWorking(undefined))
91 }, [setError, setIsWorking, step])
92
93 return [data, requestData, clearData]
94 }
95
96 export interface MigrationDataContext {
97 error: MigrationError | undefined
98 importSnippets: VoidFunction
99 importers: Importer[]
100 isWorking: MigrationStep | undefined
101 changeSelectedImporter: (newImporter: string) => void
102 snippetSelection: UseSelection<ImportableSnippet, ImportableSnippet['id']>
103 importedIds: number[]
104 }
105
106 const [Context, useMigrationData] = createContextHook<MigrationDataContext>('useMigrationData')
107
108 export const WithMigrationData: React.FC<PropsWithChildren> = ({ children }) => {
109 const { api } = useRestAPI()
110 const { selectedImporter, setSelectedImporter, autoAddTags, tagValue } = useMigrationOptions()
111
112 const [error, setError] = useState<MigrationError>()
113 const [isWorking, setIsWorking] = useState<MigrationStep>()
114
115 const [importedIds, doImport] = useRemoteRequest<number>(MigrationStep.MigrateSnippets, setError, setIsWorking)
116 const [importers, fetchImporters] = useRemoteRequest<Importer>(MigrationStep.LoadImporters, setError, setIsWorking)
117 const [snippets, fetchSnippets, resetSnippets] =
118 useRemoteRequest<ImportableSnippet>(MigrationStep.FetchSnippets, setError, setIsWorking)
119
120 const snippetSelection = useSelection(snippets, snippet => snippet.table_data.id)
121
122 useEffect(() => {
123 fetchImporters(() => api
124 .get<Record<string, Importer>>(REST_BASES.import.plugins)
125 .then(response => Object.values(response)))
126 }, [api, fetchImporters])
127
128 useEffect(() => {
129 if (selectedImporter) {
130 fetchSnippets(() => api.get<ImportableSnippet[]>(`${REST_BASES.import.plugins}/${selectedImporter}`))
131 }
132 }, [api, selectedImporter, fetchSnippets])
133
134 const importSnippets = () => {
135 const request: ImportRequest = {
136 ids: Array.from(snippetSelection.selectedItems),
137 network: isNetworkAdmin(),
138 auto_add_tags: autoAddTags,
139 tag_value: autoAddTags ? tagValue : undefined
140 }
141
142 const handleImportSuccess = () => {
143 resetSnippets()
144 snippetSelection.clearSelection()
145 }
146
147 doImport(() => makeMigrationRequest(api, selectedImporter, request), handleImportSuccess)
148 }
149
150 const changeSelectedImporter = (newImporter: string) => {
151 setSelectedImporter(newImporter)
152 snippetSelection.clearSelection()
153 setError(undefined)
154 resetSnippets()
155 }
156
157 const value: MigrationDataContext = {
158 error,
159 snippetSelection,
160 importSnippets,
161 importers,
162 isWorking,
163 changeSelectedImporter,
164 importedIds
165 }
166
167 return <Context.Provider value={value}>{children}</Context.Provider>
168 }
169
170 export { useMigrationData }
171