import { useState, useRef, useCallback } from 'react'; import { ContentObject } from '@vertesia/common'; import { Button, useToast, useTheme } from '@vertesia/ui/core'; import { useUserSession } from '@vertesia/ui/session'; import { useNavigate } from '@vertesia/ui/router'; import { MonacoEditor, IEditorApi } from '@vertesia/ui/widgets'; import { useUITranslation } from '../../../../i18n/index.js'; import { SaveVersionConfirmModal } from './SaveVersionConfirmModal.js'; interface TextEditorPanelProps { object: ContentObject; text: string; onClose: () => void; onSaved: () => void; } function getMonacoLanguage(contentType?: string): string { switch (contentType) { case 'text/markdown': return 'markdown'; case 'application/json': return 'json'; case 'application/xml': case 'text/xml': return 'xml'; default: return 'plaintext'; } } export function TextEditorPanel({ object, text, onClose, onSaved }: TextEditorPanelProps) { const { store } = useUserSession(); const toast = useToast(); const { t } = useUITranslation(); const { theme } = useTheme(); const navigate = useNavigate(); const editorRef = useRef(undefined); const [isDirty, setIsDirty] = useState(false); const [isSaving, setIsSaving] = useState(false); const [showConfirmation, setShowConfirmation] = useState(false); const language = getMonacoLanguage(object.content?.type); console.log('Determined language for Monaco Editor:', language); console.log('TextEditorPanel rendered with object:', object, text); const handleEditorChange = useCallback(() => { if (!isDirty) setIsDirty(true); }, [isDirty]); function handleSave() { if (!editorRef.current) return; setShowConfirmation(true); } async function saveText(createVersion: boolean, versionLabel?: string) { if (!editorRef.current) return; const editorText = editorRef.current.getValue(); const contentType = object.content?.type || 'text/plain'; const fileName = object.content?.name || 'content.txt'; try { setIsSaving(true); const blob = new Blob([editorText], { type: contentType }); const file = new File([blob], fileName, { type: contentType }); const response = await store.objects.update(object.id, { content: file as any, }, { createRevision: createVersion, revisionLabel: versionLabel, ifMatch: object.content?.etag, }); toast({ status: 'success', title: t('store.textSaved'), duration: 2000, }); setShowConfirmation(false); if (createVersion && response.id !== object.id) { onClose(); setTimeout(() => { navigate(`/objects/${response.id}`); }, 100); } else { onSaved(); } } catch (error: any) { const is412 = error?.status === 412 || error?.message?.includes('412'); toast({ status: 'error', title: t('store.errorSavingText'), description: is412 ? t('store.textConflict') : (error.message || t('store.errorSavingTextDefault')), duration: 5000, }); } finally { setIsSaving(false); } } return ( <>
{isDirty && ( {t('store.unsavedChanges')} )}
setShowConfirmation(false)} onConfirm={saveText} isLoading={isSaving} /> ); }