Skip to content

Annotator Data Incongruity Fix#477

Merged
thehabes merged 6 commits intomainfrom
334-annotator-incongruity
Feb 18, 2026
Merged

Annotator Data Incongruity Fix#477
thehabes merged 6 commits intomainfrom
334-annotator-incongruity

Conversation

@thehabes
Copy link
Member

@thehabes thehabes commented Feb 17, 2026

Closes #334

Summary

Fixes a data synchronization bug in the annotator where cached annotation data fell out of sync with the server after saves. This caused subsequent saves (without a page refresh) to send stale IDs, producing duplicate or incorrect annotations on the server. Also fixes a button-mashing bug on "Delete All Annotations".

Changes

Improved annotation matching after save

The previous merge logic matched server response items to local items by target string equality, which broke when targets were objects (expanded selectors). The new logic matches by annotation id first (for previously-saved annotations), then falls back to matching by selector.value (for newly-created annotations that don't yet have a server-assigned ID).

Post-save state sync with Annotorious

After a successful save, the component now:

  1. Updates #resolvedAnnotationPage with the merged server response (including new IDs)
  2. Deep-clones the items, reformats them for Annotorious, and reloads them into the Annotorious instance

This ensures subsequent saves use the correct server-assigned IDs instead of stale local ones.

Refactored error handling

Replaced the .then().catch() promise chain with an explicit try/catch around the fetch call. This makes the error boundary clear — post-save sync code only executes on a successful server response.

Moved $isDirty reset

$isDirty = false is now set after the Annotorious sync completes rather than at the end of the method, ensuring the page isn't marked clean if the sync fails.

Prevented button-mashing on "Delete All Annotations"

The delete button now disables itself and shows "deleting. please wait..." while the async operation runs. Both saveAnnotations() and clearColumnsServerSide() are wrapped in a single try/catch so errors from either are handled. A finally block re-enables the button regardless of outcome.

Test plan

Start without lines. Draw a single column and click "Save Annotations". Note the state of the AnnotationPage after the save completes. Without refreshing the page, draw a second column and click "Save Annotations". Compare the state of the AnnotationPage after this save with the state of the AnnotationPage after the first save. The first Annotation should have persisted and should have the same URI.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 17, 2026

@thehabes thehabes linked an issue Feb 17, 2026 that may be closed by this pull request
@thehabes thehabes self-assigned this Feb 17, 2026
@thehabes thehabes marked this pull request as ready for review February 17, 2026 20:15
@thehabes thehabes requested a review from cubap February 17, 2026 20:15
Copy link
Member

@cubap cubap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this does more than it should. Feel free to disagree, but I didn't want to just approve it.

const pageID = page["@id"] ?? page.id
const mod = await fetch(`${TPEN.servicesURL}/project/${TPEN.activeProject._id}/page/${pageID.split("/").pop()}`, {
let mod
try {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is weird. The fetch.then.catch is replaced with a try{fetch.then}catch{} and it looks like nothing is really changed until after this block around line 1109.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It believed that the try/catch was more explicit and I also think it thought that was the most reliable way to stop code execution. In practice the old way works the same, so I reverted this change.

message: "Could not delete all annotations.",
status: "error"
})
} finally {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes sense.

@thehabes thehabes merged commit fb6e8c5 into main Feb 18, 2026
3 checks passed
@thehabes thehabes deleted the 334-annotator-incongruity branch February 18, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Annotator Data Incongruity

2 participants

Comments