Made overrideId work for long delays

This commit is contained in:
Rainer Simon 2020-05-01 11:44:01 +02:00
parent 5554a54df8
commit 2ca27b285d
3 changed files with 61 additions and 27 deletions

View File

@ -14,8 +14,6 @@ export default class TextAnnotator extends Component {
state = { state = {
selectionBounds: null, selectionBounds: null,
selectedAnnotation: null, selectedAnnotation: null,
showRelationEditor: false,
selectedRelation: null, selectedRelation: null,
applyTemplate: null, applyTemplate: null,
@ -77,22 +75,59 @@ export default class TextAnnotator extends Component {
/** /**
* A convenience method that allows the external application to * A convenience method that allows the external application to
* override the autogenerated Id. * override the autogenerated Id for an annotation.
* *
* Usually, the override will happen almost immediately after * Usually, the override will happen almost immediately after
* the annotation is created. But we need to be defensive and assume * the annotation is created. But we need to be defensive and assume
* that the override might come in with considerable delay, thus * that the override might come in with considerable delay, thus
* the user might have made further edits already. * the user might have made further edits already.
*
* A key challenge here is that there may be dependencies between
* the original annotation and relations that were created meanwhile.
*/ */
overrideId = originalId => forcedId => { overrideAnnotationId = originalAnnotation => forcedId => {
// Force the editors to close, otherwise their annotations will be orphaned const { id } = originalAnnotation;
// After the annotation update, we need to update dependencies
// on the annotation layer, if any
const updateDependentRelations = updatedAnnotation => {
// Wait until the highlighter update has come into effect
requestAnimationFrame(() => {
this.relationsLayer.overrideTargetAnnotation(originalAnnotation, updatedAnnotation);
})
};
// Force the editors to close first, otherwise their annotations will be orphaned
if (this.state.selectedAnnotation || this.state.selectedRelation) { if (this.state.selectedAnnotation || this.state.selectedRelation) {
this.relationsLayer.resetDrawing();
this.setState({ this.setState({
selectedAnnotation: null, selectedAnnotation: null,
selectedRelation: null selectedRelation: null
}, () => this.highlighter.overrideId(originalId, forcedId)); }, () => {
const updated = this.highlighter.overrideId(id, forcedId);
updateDependentRelations(updated);
});
} else { } else {
this.highlighter.overrideId(originalId, forcedId); const updated = this.highlighter.overrideId(id, forcedId);
updateDependentRelations(updated);
}
}
/**
* A convenience method that allows the external application to
* override the autogenerated Id for a relation.
*
* This operation is less problematic than .overrideAnnotation().
* We just need to make sure the RelationEditor is closed, so that
* the annotation doesn't become orphaned. Otherwise, there are
* no dependencies.
*/
overrideRelationId = originalId => forcedId => {
if (this.state.selectedRelation) {
this.setState({ selectedRelation: null }, () =>
this.relationsLayer.overrideId(originalId, forcedId));
} else {
this.relationsLayer.overrideId(originalId, forcedId);
} }
} }
@ -107,7 +142,7 @@ export default class TextAnnotator extends Component {
if (previous) if (previous)
this.props[method](annotation.clone(), previous.clone()); this.props[method](annotation.clone(), previous.clone());
else else
this.props[method](annotation.clone(), this.overrideId(annotation.id)); this.props[method](annotation.clone(), this.overrideAnnotationId(annotation));
} }
onDeleteAnnotation = annotation => { onDeleteAnnotation = annotation => {
@ -133,7 +168,7 @@ export default class TextAnnotator extends Component {
// Shorthand // Shorthand
closeRelationsEditor = () => { closeRelationsEditor = () => {
this.setState({ showRelationEditor: false }); this.setState({ selectedRelation: null });
this.relationsLayer.resetDrawing(); this.relationsLayer.resetDrawing();
} }
@ -143,7 +178,6 @@ export default class TextAnnotator extends Component {
*/ */
onEditRelation = relation => { onEditRelation = relation => {
this.setState({ this.setState({
showRelationEditor: true,
selectedRelation: relation selectedRelation: relation
}); });
} }
@ -158,13 +192,8 @@ export default class TextAnnotator extends Component {
// otherwise, fire 'update' // otherwise, fire 'update'
const isNew = previous.annotation.bodies.length === 0; const isNew = previous.annotation.bodies.length === 0;
// A convenience method that allows the external application to
// override the autogenerated Id
const overrideId = originalId => forcedId =>
this.relationsLayer.overrideId(originalId, forcedId);
if (isNew) if (isNew)
this.props.onAnnotationCreated(relation.annotation.clone(), overrideId(relation.annotation.id)); this.props.onAnnotationCreated(relation.annotation.clone(), this.overrideRelationId(relation.annotation.id));
else else
this.props.onAnnotationUpdated(relation.annotation.clone(), previous.annotation.clone()); this.props.onAnnotationUpdated(relation.annotation.clone(), previous.annotation.clone());
} }
@ -214,7 +243,7 @@ export default class TextAnnotator extends Component {
this.relationsLayer.readOnly = false; this.relationsLayer.readOnly = false;
this.relationsLayer.startDrawing(); this.relationsLayer.startDrawing();
} else { } else {
this.setState({ showRelationEditor: false }); this.setState({ selectedRelation: null });
this.selectionHandler.enabled = true; this.selectionHandler.enabled = true;
@ -247,7 +276,7 @@ export default class TextAnnotator extends Component {
</Editor> </Editor>
} }
{ this.state.showRelationEditor && { this.state.selectedRelation &&
<RelationEditor <RelationEditor
relation={this.state.selectedRelation} relation={this.state.selectedRelation}
onRelationCreated={this.onCreateOrUpdateRelation} onRelationCreated={this.onCreateOrUpdateRelation}

View File

@ -95,10 +95,8 @@ export default class Highlighter {
* *
* @returns the updated annotation for convenience * @returns the updated annotation for convenience
*/ */
overrideId = (annotationOrId, forcedId) => { overrideId = (originalId, forcedId) => {
const id = annotationOrId.id ? annotationOrId.id : annotationOrId; const allSpans = document.querySelectorAll(`.r6o-annotation[data-id="${originalId}"]`);
const allSpans = document.querySelectorAll(`.r6o-annotation[data-id="${id}"]`);
const annotation = allSpans[0].annotation; const annotation = allSpans[0].annotation;
const updatedAnnotation = annotation.clone({ id : forcedId }); const updatedAnnotation = annotation.clone({ id : forcedId });

View File

@ -93,16 +93,23 @@ export default class RelationsLayer extends EventEmitter {
} }
} }
overrideId = (annotationOrId, forcedId) => { /** Overrides the ID for an existing relation **/
const id = annotationOrId.id ? annotationOrId.id : annotationOrId; overrideRelationId = (originalId, forcedId) => {
const conn = this.connections.find(c => c.annotation.id == id); const conn = this.connections.find(c => c.annotation.id == originalId);
const updatedAnnotation = conn.annotation.clone({ id : forcedId }); const updatedAnnotation = conn.annotation.clone({ id : forcedId });
conn.annotation = updatedAnnotation; conn.annotation = updatedAnnotation;
return conn; return conn;
} }
/** Overrides the given source or target annotation **/
overrideTargetAnnotation = (originalAnnotation, forcedAnnotation) => {
const affectedFrom = this.connections.filter(c => c.fromNode.annotation == originalAnnotation);
affectedFrom.forEach(c => c.fromNode.annotation = forcedAnnotation);
const affectedTo = this.connections.filter(c => c.toNode.annotation == originalAnnotation);
affectedTo.forEach(c => c.toNode.annotation = forcedAnnotation);
}
getAllRelations = () => { getAllRelations = () => {
return this.connections.map(c => c.annotation); return this.connections.map(c => c.annotation);
} }