history tweaks

This commit is contained in:
Reece 2025-09-25 09:46:20 +01:00
parent 3d2607f72a
commit a8a0808274
3 changed files with 135 additions and 34 deletions

View File

@ -2,6 +2,7 @@ import React, { useImperativeHandle, forwardRef, useEffect } from 'react';
import { useHistoryCapability } from '@embedpdf/plugin-history/react';
import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react';
import { useSignature } from '../../contexts/SignatureContext';
import { uuidV4 } from '@embedpdf/models';
export interface HistoryAPI {
undo: () => void;
@ -15,7 +16,80 @@ export interface HistoryAPIBridgeProps {}
export const HistoryAPIBridge = forwardRef<HistoryAPI, HistoryAPIBridgeProps>((props, ref) => {
const { provides: historyApi } = useHistoryCapability();
const { provides: annotationApi } = useAnnotationCapability();
const { getImageData } = useSignature();
const { getImageData, storeImageData } = useSignature();
// Monitor annotation events to detect when annotations are restored
useEffect(() => {
if (!annotationApi) return;
const handleAnnotationEvent = (event: any) => {
const annotation = event.annotation;
// Store image data for all STAMP annotations immediately when created or modified
if (annotation && annotation.type === 13 && annotation.id && annotation.imageSrc) {
const storedImageData = getImageData(annotation.id);
if (!storedImageData || storedImageData !== annotation.imageSrc) {
console.log('HistoryAPI: Storing image data for annotation', annotation.id);
storeImageData(annotation.id, annotation.imageSrc);
}
}
// Handle annotation restoration after undo operations
if (event.type === 'create' && event.committed) {
// Check if this is a STAMP annotation (signature) that might need image data restoration
if (annotation && annotation.type === 13 && annotation.id) {
const storedImageData = getImageData(annotation.id);
// Delay the check to allow the annotation to be fully created
setTimeout(() => {
const currentStoredData = getImageData(annotation.id);
// Check if the annotation lacks image data but we have it stored
if (currentStoredData && (!annotation.imageSrc || annotation.imageSrc !== currentStoredData)) {
console.log('HistoryAPI: Restoring image data for annotation', annotation.id);
// Generate new ID to avoid React key conflicts
const newId = uuidV4();
// Recreation with stored image data
const restoredData = {
type: annotation.type,
rect: annotation.rect,
author: annotation.author || 'Digital Signature',
subject: annotation.subject || 'Digital Signature',
pageIndex: event.pageIndex,
id: newId,
created: annotation.created || new Date(),
imageSrc: currentStoredData
};
// Update stored data to use new ID
storeImageData(newId, currentStoredData);
// Replace the annotation with one that has proper image data
try {
annotationApi.deleteAnnotation(event.pageIndex, annotation.id);
// Small delay to ensure deletion completes
setTimeout(() => {
annotationApi.createAnnotation(event.pageIndex, restoredData);
}, 50);
} catch (error) {
console.error('HistoryAPI: Failed to restore annotation:', error);
}
}
}, 100);
}
}
};
// Add the event listener
annotationApi.onAnnotationEvent(handleAnnotationEvent);
// Cleanup function
return () => {
// Note: EmbedPDF doesn't provide a way to remove event listeners
// This is a limitation of the current API
};
}, [annotationApi, getImageData, storeImageData]);
useImperativeHandle(ref, () => ({
@ -24,30 +98,43 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI, HistoryAPIBridgeProps>((p
historyApi.undo();
// Restore image data for STAMP annotations after undo
// This handles both manual undo and delete+undo scenarios
setTimeout(() => {
if (!annotationApi) return;
for (let pageIndex = 0; pageIndex < 10; pageIndex++) {
// Check reasonable number of pages - most documents have fewer than 10 pages
for (let pageIndex = 0; pageIndex < 5; pageIndex++) {
const pageAnnotationsTask = annotationApi.getPageAnnotations?.({ pageIndex });
if (pageAnnotationsTask) {
pageAnnotationsTask.toPromise().then((pageAnnotations: any) => {
if (pageAnnotations) {
if (pageAnnotations && pageAnnotations.length > 0) {
pageAnnotations.forEach((ann: any) => {
if (ann.type === 13) {
if (ann.type === 13) { // STAMP annotations
const storedImageData = getImageData(ann.id);
if (storedImageData && (!ann.imageSrc || ann.imageSrc !== storedImageData)) {
// Generate new ID to avoid React key conflicts
const newId = uuidV4();
const originalData = {
type: ann.type,
rect: ann.rect,
author: ann.author || 'Digital Signature',
subject: ann.subject || 'Digital Signature',
pageIndex: pageIndex,
id: ann.id,
id: newId,
created: ann.created || new Date(),
imageSrc: storedImageData
imageSrc: storedImageData,
// Store in multiple fields to ensure compatibility
contents: storedImageData,
data: storedImageData,
imageData: storedImageData,
appearance: storedImageData
};
// Update stored data to use new ID
storeImageData(newId, storedImageData);
annotationApi.deleteAnnotation(pageIndex, ann.id);
setTimeout(() => {
annotationApi.createAnnotation(pageIndex, originalData);
@ -56,8 +143,8 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI, HistoryAPIBridgeProps>((p
}
});
}
}).catch((error: any) => {
console.error(`Failed to get annotations for page ${pageIndex}:`, error);
}).catch(() => {
// Silently ignore "Page not found" errors for non-existent pages
});
}
}

View File

@ -32,37 +32,41 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
const selectedAnnotation = annotationApi.getSelectedAnnotation?.();
if (selectedAnnotation) {
// First, let EmbedPDF try to handle this natively
// Only intervene if needed
setTimeout(() => {
// Check if the annotation is still selected after a brief delay
// If EmbedPDF handled it natively, it should be gone
const stillSelected = annotationApi.getSelectedAnnotation?.();
const annotation = selectedAnnotation as any;
const pageIndex = annotation.object?.pageIndex || 0;
const id = annotation.object?.id;
if (stillSelected) {
// EmbedPDF didn't handle it, so we need to delete manually
const annotation = stillSelected as any;
const pageIndex = annotation.object?.pageIndex || 0;
const id = annotation.object?.id;
if (id) {
// Try deleteSelected method first (should integrate with history)
if ((annotationApi as any).deleteSelected) {
(annotationApi as any).deleteSelected();
} else {
// Fallback to direct deletion
annotationApi.deleteAnnotation(pageIndex, id);
// For STAMP annotations, ensure image data is preserved before deletion
if (annotation.object?.type === 13 && id) {
// Get current annotation data to ensure we have latest image data stored
const pageAnnotationsTask = annotationApi.getPageAnnotations?.({ pageIndex });
if (pageAnnotationsTask) {
pageAnnotationsTask.toPromise().then((pageAnnotations: any) => {
const currentAnn = pageAnnotations?.find((ann: any) => ann.id === id);
if (currentAnn && currentAnn.imageSrc) {
// Ensure the image data is stored in our persistent store
storeImageData(id, currentAnn.imageSrc);
}
}
}).catch(console.error);
}
}, 10);
}
// Use EmbedPDF's native deletion which should integrate with history
if ((annotationApi as any).deleteSelected) {
(annotationApi as any).deleteSelected();
} else {
// Fallback to direct deletion - less ideal for history
if (id) {
annotationApi.deleteAnnotation(pageIndex, id);
}
}
}
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [annotationApi]);
}, [annotationApi, storeImageData]);
useImperativeHandle(ref, () => ({
addImageSignature: (signatureData: string, x: number, y: number, width: number, height: number, pageIndex: number) => {
@ -253,6 +257,19 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
deleteAnnotation: (annotationId: string, pageIndex: number) => {
if (!annotationApi) return;
// Before deleting, try to preserve image data for potential undo
const pageAnnotationsTask = annotationApi.getPageAnnotations?.({ pageIndex });
if (pageAnnotationsTask) {
pageAnnotationsTask.toPromise().then((pageAnnotations: any) => {
const annotation = pageAnnotations?.find((ann: any) => ann.id === annotationId);
if (annotation && annotation.type === 13 && annotation.imageSrc) {
// Store image data before deletion
storeImageData(annotationId, annotation.imageSrc);
}
}).catch(console.error);
}
// Delete specific annotation by ID
annotationApi.deleteAnnotation(pageIndex, annotationId);
},

View File

@ -121,14 +121,11 @@ export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children
}, []);
const storeImageData = useCallback((id: string, data: string) => {
console.log('Storing image data for annotation:', id, data.length, 'chars');
imageDataStore.current.set(id, data);
}, []);
const getImageData = useCallback((id: string) => {
const data = imageDataStore.current.get(id);
console.log('Retrieving image data for annotation:', id, data?.length, 'chars');
return data;
return imageDataStore.current.get(id);
}, []);