| 1 |
import { useCallback } from "react"; |
| 2 |
import { useQuery, useQueryClient } from "@tanstack/react-query"; |
| 3 |
import apiFetch from "@wordpress/api-fetch"; |
| 4 |
import { useSelect } from "@wordpress/data"; |
| 5 |
import { store as editorStore } from "@wordpress/editor"; |
| 6 |
import { BindingSource, ElementBindings } from "../types"; |
| 7 |
import { useTableStore } from "../../store"; |
| 8 |
|
| 9 |
function resolvePostId( |
| 10 |
binding: BindingSource | undefined, |
| 11 |
contextPostId?: number |
| 12 |
): number | undefined { |
| 13 |
if (!binding) { |
| 14 |
return undefined; |
| 15 |
} |
| 16 |
return binding.postId ?? contextPostId; |
| 17 |
} |
| 18 |
|
| 19 |
async function queryDynamicData(key: string, postId?: number) { |
| 20 |
const params = new URLSearchParams({ key }); |
| 21 |
|
| 22 |
if (postId) { |
| 23 |
params.append("postId", postId.toString()); |
| 24 |
} |
| 25 |
|
| 26 |
const response = await apiFetch<{ |
| 27 |
success: boolean; |
| 28 |
value: string | null; |
| 29 |
}>({ |
| 30 |
path: `/tableberg/v1/dynamic-data/preview?${params.toString()}`, |
| 31 |
}); |
| 32 |
|
| 33 |
return response; |
| 34 |
} |
| 35 |
|
| 36 |
export function useDynamicData( |
| 37 |
binding: BindingSource | undefined, |
| 38 |
contextPostId?: number |
| 39 |
) { |
| 40 |
const queryClient = useQueryClient(); |
| 41 |
const postId = resolvePostId(binding, contextPostId); |
| 42 |
|
| 43 |
const query = useQuery({ |
| 44 |
queryKey: ["dynamicData", binding?.key, postId], |
| 45 |
queryFn: () => (binding ? queryDynamicData(binding.key, postId) : null), |
| 46 |
enabled: !!binding && !!postId, |
| 47 |
staleTime: Infinity, |
| 48 |
}); |
| 49 |
|
| 50 |
const refresh = useCallback(async () => { |
| 51 |
if (!binding) return; |
| 52 |
await queryClient.invalidateQueries({ |
| 53 |
queryKey: ["dynamicData", binding.key, postId], |
| 54 |
}); |
| 55 |
}, [binding, postId, queryClient]); |
| 56 |
|
| 57 |
return { |
| 58 |
data: query.data?.value ?? null, |
| 59 |
isLoading: query.isLoading, |
| 60 |
isCached: query.data?.value !== null && !query.isFetching, |
| 61 |
refresh, |
| 62 |
}; |
| 63 |
} |
| 64 |
|
| 65 |
export function useDynamicDataBindings(bindings: ElementBindings | undefined) { |
| 66 |
const queryClient = useQueryClient(); |
| 67 |
const bindingDefinitions = useTableStore(state => state.bindings); |
| 68 |
|
| 69 |
const currentPostId = useSelect(select => { |
| 70 |
const editorSelect = select(editorStore) as { |
| 71 |
getCurrentPostId?: () => number | undefined; |
| 72 |
}; |
| 73 |
return editorSelect.getCurrentPostId?.() ?? undefined; |
| 74 |
}, []); |
| 75 |
|
| 76 |
const resolvedBindings = Object.entries(bindings || {}).reduce< |
| 77 |
Record<string, BindingSource> |
| 78 |
>((acc, [path, bindingId]) => { |
| 79 |
const binding = bindingDefinitions[bindingId]; |
| 80 |
if (binding) { |
| 81 |
acc[path] = binding; |
| 82 |
} |
| 83 |
return acc; |
| 84 |
}, {}); |
| 85 |
|
| 86 |
const query = useQuery({ |
| 87 |
queryKey: ["dynamicDataBindings", currentPostId, resolvedBindings], |
| 88 |
queryFn: async () => { |
| 89 |
if (!bindings) return {}; |
| 90 |
const newValues: Record<string, string | null> = {}; |
| 91 |
await Promise.all( |
| 92 |
Object.entries(resolvedBindings).map( |
| 93 |
async ([path, binding]) => { |
| 94 |
try { |
| 95 |
const postId = resolvePostId( |
| 96 |
binding, |
| 97 |
currentPostId |
| 98 |
); |
| 99 |
const response = await queryDynamicData( |
| 100 |
binding.key, |
| 101 |
postId |
| 102 |
); |
| 103 |
if (response.success) { |
| 104 |
newValues[path] = |
| 105 |
response.value ?? binding.fallback ?? null; |
| 106 |
} |
| 107 |
} catch {} |
| 108 |
} |
| 109 |
) |
| 110 |
); |
| 111 |
return newValues; |
| 112 |
}, |
| 113 |
enabled: Object.keys(resolvedBindings).length > 0 && !!currentPostId, |
| 114 |
staleTime: Infinity, |
| 115 |
}); |
| 116 |
|
| 117 |
const refreshAll = useCallback(async () => { |
| 118 |
if (!bindings) return; |
| 119 |
await queryClient.invalidateQueries({ |
| 120 |
queryKey: ["dynamicDataBindings", currentPostId, resolvedBindings], |
| 121 |
}); |
| 122 |
}, [bindings, currentPostId, queryClient, resolvedBindings]); |
| 123 |
|
| 124 |
return { |
| 125 |
values: (query.data as Record<string, string | null>) || {}, |
| 126 |
isLoading: query.isLoading, |
| 127 |
isCached: Object.keys(query.data || {}).length > 0 && !query.isFetching, |
| 128 |
refreshAll, |
| 129 |
}; |
| 130 |
} |
| 131 |
|
| 132 |
export async function fetchAvailableMetaKeys( |
| 133 |
postId?: number |
| 134 |
): Promise<Array<{ key: string; label?: string }>> { |
| 135 |
const params = new URLSearchParams(); |
| 136 |
if (postId) { |
| 137 |
params.append("postId", postId.toString()); |
| 138 |
} |
| 139 |
|
| 140 |
const query = params.toString(); |
| 141 |
const path = `/tableberg/v1/dynamic-data/meta-keys${query ? `?${query}` : ""}`; |
| 142 |
|
| 143 |
try { |
| 144 |
const response = await apiFetch<{ |
| 145 |
keys: Array<{ key: string; label?: string }>; |
| 146 |
}>({ path }); |
| 147 |
return response.keys; |
| 148 |
} catch { |
| 149 |
return []; |
| 150 |
} |
| 151 |
} |
| 152 |
|
| 153 |
export { useDynamicData as default }; |
| 154 |
|