mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-26 17:52:59 +02:00
history tweaks
This commit is contained in:
parent
3d2607f72a
commit
a8a0808274
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -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);
|
||||
}, []);
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user