import { createElement, useEffect, useState } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import type { EditorSnapshot } from '../extensions/settings-tab-registry'; import type { RevisionDetailResponse, RevisionListResponse, RevisionSection, RevisionSummary, } from '../types/rest'; type ApiFetch = (options: { url: string; method?: string }) => Promise; type HistoryPanelProps = { postId: number; restUrl: string; apiFetch: ApiFetch; supported: boolean; currentVersion: string; canUpdateCore: boolean; updateCoreUrl: string; refreshToken: number; hasUnsavedChanges: () => boolean; onLoadSnapshot: (snapshot: EditorSnapshot) => boolean; }; const SECTION_LABELS: Record = { html: 'HTML', css: 'CSS', javascript: 'JavaScript', customHead: 'Head', }; function appendQuery(url: string, params: Record) { const target = new URL(url, window.location.href); Object.entries(params).forEach(([key, value]) => target.searchParams.set(key, String(value))); return target.toString(); } export function HistoryPanel(props: HistoryPanelProps) { const [items, setItems] = useState([]); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(0); const [revisionsEnabled, setRevisionsEnabled] = useState(true); const [canLoad, setCanLoad] = useState(false); const [loading, setLoading] = useState(false); const [loadingRevisionId, setLoadingRevisionId] = useState(null); const [error, setError] = useState(''); const loadPage = async (nextPage: number, append: boolean) => { setLoading(true); setError(''); try { const response = await props.apiFetch({ url: appendQuery(props.restUrl, { post_id: props.postId, page: nextPage, per_page: 20 }), }); setItems((current) => (append ? [...current, ...response.revisions] : response.revisions)); setPage(response.pagination.page); setTotalPages(response.pagination.totalPages); setRevisionsEnabled(response.revisionsEnabled); setCanLoad(response.canLoad); } catch (reason) { setError(reason instanceof Error ? reason.message : __('Failed to load history.', 'kayzart-live-code-editor')); } finally { setLoading(false); } }; useEffect(() => { if (!props.supported) { return; } void loadPage(1, false); // loadPage intentionally depends on the immutable editor configuration. // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.supported, props.refreshToken]); const loadRevision = async (revision: RevisionSummary) => { if (!canLoad || loadingRevisionId !== null) { return; } if ( props.hasUnsavedChanges() && !window.confirm( __('Loading this version will discard your current unsaved changes. Continue?', 'kayzart-live-code-editor') ) ) { return; } setLoadingRevisionId(revision.id); setError(''); try { const response = await props.apiFetch({ url: appendQuery(`${props.restUrl}/${revision.id}`, { post_id: props.postId }), }); if (!response.ok || !response.revision) { throw new Error(response.error || __('Failed to load revision.', 'kayzart-live-code-editor')); } props.onLoadSnapshot(response.revision.snapshot); } catch (reason) { setError(reason instanceof Error ? reason.message : __('Failed to load revision.', 'kayzart-live-code-editor')); } finally { setLoadingRevisionId(null); } }; if (!props.supported) { return (
{__('Full-page revisions require WordPress 6.4 or later.', 'kayzart-live-code-editor')}

{sprintf( __('This site is running WordPress %s.', 'kayzart-live-code-editor'), props.currentVersion || __('Unknown', 'kayzart-live-code-editor') )}

{props.canUpdateCore && props.updateCoreUrl ? ( {__('Update WordPress', 'kayzart-live-code-editor')} ) : (

{__('Contact a site administrator to update WordPress.', 'kayzart-live-code-editor')}

)}
); } if (!revisionsEnabled && !loading && items.length === 0) { return (
{__('Revisions are disabled for this site.', 'kayzart-live-code-editor')}

{__('Enable WordPress revisions to keep full-page history.', 'kayzart-live-code-editor')}

); } return (
{!revisionsEnabled ? (
{__('Revisions are disabled for this site.', 'kayzart-live-code-editor')}

{__('Enable WordPress revisions to keep full-page history.', 'kayzart-live-code-editor')}

) : null} {!canLoad ? (
{__('You can view history, but loading a complete version requires unfiltered HTML permission.', 'kayzart-live-code-editor')}
) : null} {error ?
{error}
: null} {!loading && items.length === 0 ? (
{__('No full-page revisions yet.', 'kayzart-live-code-editor')}
) : null}
{items.map((revision) => (
{new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(revision.date))} {revision.author.name}
{revision.isFirst ? {__('First snapshot', 'kayzart-live-code-editor')} : null} {revision.changedSections.map((section) => ( {SECTION_LABELS[section]} ))}
))}
{page < totalPages ? ( ) : null}
); }