Skip to content
Snippets Groups Projects
Commit c60a48be authored by Anthony's avatar Anthony
Browse files

Refactoring saving annotation

parent f88a29c5
No related branches found
No related tags found
1 merge request!10Draft: MigratingAnnotationCreation to MUI5.
import React, { useEffect, useLayoutEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
Button,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import { v4 as uuid } from 'uuid';
import { exportStageSVG } from 'react-konva-to-svg';
import CompanionWindow from 'mirador/dist/es/src/containers/CompanionWindow';
import { OSDReferences } from 'mirador/dist/es/src/plugins/OSDReferences';
import { VideosReferences } from 'mirador/dist/es/src/plugins/VideosReferences';
......@@ -16,13 +11,13 @@ import LocalOfferIcon from '@mui/icons-material/LocalOffer';
import HubIcon from '@mui/icons-material/Hub';
import { TabContext, TabList, TabPanel } from '@mui/lab';
import AnnotationDrawing from './annotationForm/AnnotationDrawing';
import { secondsToHMS } from './utils';
import AnnotationFormContent from './annotationForm/AnnotationFormContent';
import AnnotationFormTime from './annotationForm/AnnotationFormTime';
import {
geomFromAnnoTarget, getJPG, getSvg, saveAnnotation, timeFromAnnoTarget,
geomFromAnnoTarget, timeFromAnnoTarget,
} from './AnnotationCreationUtils';
import AnnotationFormOverlay from './annotationForm/AnnotationFormOverlay/AnnotationFormOverlay.js';
import AnnotationFormOverlay from './annotationForm/AnnotationFormOverlay/AnnotationFormOverlay';
import AnnotationFormFooter from './annotationForm/AnnotationFormFooter';
const TARGET_VIEW = 'target';
const OVERLAY_VIEW = 'layer';
......@@ -302,54 +297,14 @@ function AnnotationCreation(props) {
}
};
/**
* Validate form and save annotation
*/
const submitForm = async (e) => {
console.log('submitForm');
e.preventDefault();
// TODO Possibly problem of syncing
// TODO Improve this code
// If we are in edit mode, we have the transformer on the stage saved in the annotation
/* if (viewTool === OVERLAY_VIEW && state.activeTool === 'edit') {
setState((prevState) => ({
...prevState,
activeTool: 'cursor',
}));
return;
} */
const {
annotation,
canvases,
receiveAnnotation,
config,
} = props;
const drawingStateSerialized = JSON.stringify(drawingState);
const {
textBody,
tags,
xywh,
tstart,
tend,
image,
} = state;
// TODO rename variable for better comprenhension
const svg = await getSvg(props.windowId);
// const jpg = await getJPG(props.windowId);
const drawingImageExport = svg;
const t = (tstart && tend) ? `${tstart},${tend}` : null;
const body = { value: (!textBody.length && t) ? `${secondsToHMS(tstart)} -> ${secondsToHMS(tend)}` : textBody };
saveAnnotation(canvases, config, receiveAnnotation, annotation, body, t, xywh, image, drawingStateSerialized, drawingImageExport, tags);
props.closeCompanionWindow('annotationCreation', {
id,
const closeFormCompanionWindow = () => {
closeCompanionWindow('annotationCreation', {
id: props.id,
position: 'right',
});
};
const resetStateAfterSave = () => {
// TODO this create a re-render too soon for react and crash the app
setState({
image: { id: null },
......@@ -360,7 +315,7 @@ function AnnotationCreation(props) {
tstart: 0,
xywh: null,
});
};
}
/** */
const {
......@@ -447,7 +402,6 @@ function AnnotationCreation(props) {
setDrawingState={setDrawingState}
/>
<StyledForm
onSubmit={submitForm}
>
<TabContext value={viewTool}>
<TabList value={viewTool} onChange={tabHandler} aria-label="icon tabs">
......@@ -515,30 +469,22 @@ function AnnotationCreation(props) {
value={MANIFEST_LINK_VIEW}
/>
</TabContext>
<StyledButtonDivSaveOrCancel>
<Button onClick={closeCompanionWindow}>
Cancel
</Button>
<Button
variant="contained"
color="primary"
type="submit"
onMouseOver={() => setIsMouseOverSave(true)}
onMouseOut={() => setIsMouseOverSave(false)}
>
Save
</Button>
</StyledButtonDivSaveOrCancel>
<AnnotationFormFooter
annotation={annotation}
canvases={props.canvases}
closeFormCompanionWindow={closeFormCompanionWindow}
config={props.config}
drawingState={drawingState}
receiveAnnotation={props.receiveAnnotation}
resetStateAfterSave={resetStateAfterSave}
state={state}
windowId={windowId}
/>
</StyledForm>
</CompanionWindow>
);
}
const StyledButtonDivSaveOrCancel = styled('div')(({ theme }) => ({
display: 'flex',
justifyContent: 'flex-end',
}));
const StyledForm = styled('form')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
......
import { exportStageSVG } from 'react-konva-to-svg';
import { v4 as uuid } from 'uuid';
import axios from 'axios';
import WebAnnotation from './WebAnnotation';
const fileUploaderUrl = 'https://scene-uploads.tetras-libre.fr/upload';
const fileReaderUrl = 'https://scene-uploads.tetras-libre.fr/static/';
export const fileUploaderUrl = 'https://scene-uploads.tetras-libre.fr/upload';
export const fileReaderUrl = 'https://scene-uploads.tetras-libre.fr/static/';
/*const fileUploaderUrl = 'http://localhost:3000/upload';
const fileReaderUrl = 'http://localhost:3000/static/';*/
......@@ -52,55 +49,28 @@ export function isShapesTool(activeTool) {
return Object.values(SHAPES_TOOL).find((tool) => tool === activeTool);
}
/**
* Get SVG picture containing all the stuff draw in the stage (Konva Stage).
* This image will be put in overlay of the iiif media
*/
export async function getSvg(windowId) {
const stage = window.Konva.stages.find((s) => s.attrs.id === windowId);
const svg = await exportStageSVG(stage, false); // TODO clean
console.log('SVG:', svg);
return svg;
}
export async function getJPG(windowId) {
const stage = window.Konva.stages.find((s) => s.attrs.id === windowId);
const jpg = await stage.toImage({ mimeType: 'image/jpeg', quality: 1 });
console.log('JPG:', jpg);
return jpg;
}
export async function saveAnnotation(canvases, config, receiveAnnotation, annotation, body, t, xywh, image, drawingStateSerialized, drawingImageExport, tags) {
console.log('Send file :', drawingImageExport);
const filename = await sendFile(drawingImageExport);
canvases.forEach(async (canvas) => {
const storageAdapter = config.annotation.adapter(canvas.id);
const anno = {
body: {
id: fileReaderUrl + filename,
type: 'Image',
format: 'image/svg+xml',
value: body.value,
},
drawingState: drawingStateSerialized,
id: (annotation && annotation.id) || `${uuid()}`,
motivation: 'commenting',
target: `${canvas.id}#xywh=${xywh}&t=${t}`,
type: 'Annotation',
};
if (annotation) {
storageAdapter.update(anno)
/** Save annotation in the storage adapter */
export async function saveAnnotation(canvas, storageAdapter, receiveAnnotation, annotationToSaved, isNewAnnotation) {
if (isNewAnnotation) {
storageAdapter.update(annotationToSaved)
.then((annoPage) => {
receiveAnnotation(canvas.id, storageAdapter.annotationPageId, annoPage);
});
} else {
storageAdapter.create(anno)
storageAdapter.create(annotationToSaved)
.then((annoPage) => {
receiveAnnotation(canvas.id, storageAdapter.annotationPageId, annoPage);
});
}
}
export async function saveAnnotationInEachCanvas(canvases, config, receiveAnnotation, annotationToSaved, target, isNewAnnotation) {
canvases.forEach(async (canvas) => {
// Adapt target to the canvas
// eslint-disable-next-line no-param-reassign
annotationToSaved.target = `${canvas.id}#xywh=${target.xywh}&t=${target.t}`;
const storageAdapter = config.annotation.adapter(canvas.id);
saveAnnotation(canvas, storageAdapter, receiveAnnotation, annotationToSaved, isNewAnnotation);
});
}
......@@ -110,7 +80,6 @@ const sendFile = async (fileContent) => {
const formData = new FormData();
formData.append('file', blob);
try {
const response = await axios.post(fileUploaderUrl, formData, {
headers: {
......@@ -128,20 +97,11 @@ const sendFile = async (fileContent) => {
};
// export function dataURLtoBlob(dataurl) {
// // Split the Data URL to get the metadata and the actual data
// console.log('Data URL:', dataurl);
// var arr = dataurl.split(','),
// mime = arr[0].match(/:(.*?);/)[1], // Extract MIME type
// bstr = atob(arr[1]), // Decode base64
// n = bstr.length,
// u8arr = new Uint8Array(n); // Create a new ArrayBuffer
//
// // Convert the binary string to an ArrayBuffer
// while(n--){
// u8arr[n] = bstr.charCodeAt(n);
// }
//
// // Return a Blob object
// return new Blob([u8arr], {type:mime});
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
import { Button } from '@mui/material';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';
import React from 'react';
import { v4 as uuid } from 'uuid';
import {
saveAnnotationInEachCanvas,
} from '../AnnotationCreationUtils';
import { secondsToHMS } from '../utils';
import {
getJPGAsDataURL,
getKonvaAsDataURL
} from './AnnotationFormOverlay/KonvaDrawing/KonvaUtils';
const StyledButtonDivSaveOrCancel = styled('div')(({ theme }) => ({
display: 'flex',
justifyContent: 'flex-end',
}));
/** Annotation form footer, save or cancel the edition/creation of an annotation */
function AnnotationFormFooter({
annotation,
canvases,
closeFormCompanionWindow,
config,
drawingState,
receiveAnnotation,
resetStateAfterSave,
state,
windowId,
}) {
/**
* Validate form and save annotation
*/
const submitAnnotationForm = async (e) => {
console.log('submitForm');
e.preventDefault();
// TODO Possibly problem of syncing
// TODO Improve this code
// If we are in edit mode, we have the transformer on the stage saved in the annotation
/* if (viewTool === OVERLAY_VIEW && state.activeTool === 'edit') {
setState((prevState) => ({
...prevState,
activeTool: 'cursor',
}));
return;
} */
const {
textBody,
tags,
xywh,
tstart,
tend,
image,
} = state;
// Save annotation drawing in svg and sent it to the server
// const svg = await getSvg(windowId);
// const drawingImageExport = jpg;
// const filename = await sendFile(drawingImageExport);
// const annotationBodyImageId = fileReaderUrl + filename;
// Save jpg image of the drawing in a data url
const annotationBodyImageId = getKonvaAsDataURL(windowId);
// Temporal target of the annotation
const target = {
t: (tstart && tend) ? `${tstart},${tend}` : null,
xywh, // TODO retrouver calcul de xywh
};
const annotationText = (!textBody.length && target.t) ? `${secondsToHMS(tstart)} -> ${secondsToHMS(tend)}` : textBody;
console.log('annotationBodyImageId:', annotationBodyImageId);
const annotationToSaved = {
body: {
id: annotationBodyImageId,
type: 'Image',
format: 'image/svg+xml',
value: annotationText,
},
drawingState: JSON.stringify(drawingState),
id: (annotation && annotation.id) || `${uuid()}`,
motivation: 'commenting',
target: null,
type: 'Annotation', // Will be updated in saveAnnotationInEachCanvas
};
console.log('Annotation to save:', annotationToSaved);
console.log('target:', target);
const isNewAnnotation = !annotation;
saveAnnotationInEachCanvas(canvases, config, receiveAnnotation, annotationToSaved, target, isNewAnnotation);
closeFormCompanionWindow();
resetStateAfterSave();
};
return (
<StyledButtonDivSaveOrCancel>
<Button onClick={closeFormCompanionWindow}>
Cancel
</Button>
<Button
variant="contained"
color="primary"
type="submit"
onClick={submitAnnotationForm}
>
Save
</Button>
</StyledButtonDivSaveOrCancel>
);
}
AnnotationFormFooter.propTypes = {
annotation: PropTypes.object, // eslint-disable-line react/forbid-prop-types
canvases: PropTypes.arrayOf(PropTypes.object).isRequired,
closeFormCompanionWindow: PropTypes.func.isRequired,
config: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
drawingState: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
receiveAnnotation: PropTypes.func.isRequired,
resetStateAfterSave: PropTypes.func.isRequired,
state: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
windowId: PropTypes.string.isRequired,
};
export default AnnotationFormFooter;
import { exportStageSVG } from 'react-konva-to-svg';
/**
* Get SVG picture containing all the stuff draw in the stage (Konva Stage).
* This image will be put in overlay of the iiif media
*/
export async function getSvg(windowId) {
const stage = window.Konva.stages.find((s) => s.attrs.id === windowId);
const svg = await exportStageSVG(stage, false); // TODO clean
console.log('SVG:', svg);
return svg;
}
/** Export the stage as a JPG image in a data url */
export async function getKonvaAsDataURL(windowId) {
const stage = window.Konva.stages.find((s) => s.attrs.id === windowId);
const dataURL = await stage.toDataURL({ mimeType: 'image/png', quality: 1 });
console.log('dataURL:', dataURL);
return dataURL;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment