|
1 | 1 | import { |
| 2 | + acquireAnnotationMutex, |
2 | 3 | type NeighborInfo, |
3 | 4 | getAgglomeratesForSegmentsFromTracingstore, |
4 | 5 | getEdgesForAgglomerateMinCut, |
5 | 6 | getNeighborsForAgglomerateNode, |
6 | 7 | getPositionForSegmentInAgglomerate, |
| 8 | + releaseAnnotationMutex, |
7 | 9 | } from "admin/rest_api"; |
8 | 10 | import { V3 } from "libs/mjs"; |
9 | 11 | import Toast from "libs/toast"; |
@@ -358,6 +360,7 @@ function* handleSkeletonProofreadingAction(action: Action): Saga<void> { |
358 | 360 | const { agglomerateFileMag, getDataValue, activeMapping, volumeTracing } = preparation; |
359 | 361 | const { tracingId: volumeTracingId } = volumeTracing; |
360 | 362 |
|
| 363 | + const annotationId = yield* select((state) => state.annotation.annotationId); |
361 | 364 | // Use untransformedPosition because agglomerate trees should not have |
362 | 365 | // any transforms, anyway. |
363 | 366 | if (yield* select((state) => areGeometriesTransformed(state))) { |
@@ -441,6 +444,7 @@ function* handleSkeletonProofreadingAction(action: Action): Saga<void> { |
441 | 444 |
|
442 | 445 | yield* put(pushSaveQueueTransaction(items)); |
443 | 446 | yield* call([Model, Model.ensureSavedState]); |
| 447 | + yield* call(releaseAnnotationMutex, annotationId); |
444 | 448 |
|
445 | 449 | if (action.type === "MIN_CUT_AGGLOMERATE_WITH_NODE_IDS" || action.type === "DELETE_EDGE") { |
446 | 450 | if (sourceAgglomerateId !== targetAgglomerateId) { |
@@ -690,6 +694,7 @@ function* handleProofreadMergeOrMinCut(action: Action) { |
690 | 694 | const allowUpdate = yield* select((state) => state.annotation.restrictions.allowUpdate); |
691 | 695 | if (!allowUpdate) return; |
692 | 696 |
|
| 697 | + const annotationId = yield* select((state) => state.annotation.annotationId); |
693 | 698 | const preparation = yield* call(prepareSplitOrMerge, false); |
694 | 699 | if (!preparation) { |
695 | 700 | return; |
@@ -775,6 +780,7 @@ function* handleProofreadMergeOrMinCut(action: Action) { |
775 | 780 |
|
776 | 781 | yield* put(pushSaveQueueTransaction(items)); |
777 | 782 | yield* call([Model, Model.ensureSavedState]); |
| 783 | + yield* call(releaseAnnotationMutex, annotationId); |
778 | 784 |
|
779 | 785 | if (action.type === "MIN_CUT_AGGLOMERATE") { |
780 | 786 | console.log("start updating the mapping after a min-cut"); |
@@ -879,6 +885,7 @@ function* handleProofreadCutFromNeighbors(action: Action) { |
879 | 885 | const allowUpdate = yield* select((state) => state.annotation.restrictions.allowUpdate); |
880 | 886 | if (!allowUpdate) return; |
881 | 887 |
|
| 888 | + const annotationId = yield* select((state) => state.annotation.annotationId); |
882 | 889 | const preparation = yield* call(prepareSplitOrMerge, false); |
883 | 890 | if (!preparation) { |
884 | 891 | return; |
@@ -933,6 +940,7 @@ function* handleProofreadCutFromNeighbors(action: Action) { |
933 | 940 |
|
934 | 941 | yield* put(pushSaveQueueTransaction(items)); |
935 | 942 | yield* call([Model, Model.ensureSavedState]); |
| 943 | + yield* call(releaseAnnotationMutex, annotationId); |
936 | 944 |
|
937 | 945 | // Now that the changes are saved, we can split the mapping locally (because it requires |
938 | 946 | // communication with the back-end). |
@@ -1116,6 +1124,13 @@ function* prepareSplitOrMerge(isSkeletonProofreading: boolean): Saga<Preparation |
1116 | 1124 | return null; |
1117 | 1125 | } |
1118 | 1126 |
|
| 1127 | + const annotationId = yield* select((state) => state.annotation.annotationId); |
| 1128 | + const { canEdit } = yield* call(acquireAnnotationMutex, annotationId); |
| 1129 | + if (!canEdit) { |
| 1130 | + Toast.error("Could not acquire mutex. Somebody else is proofreading at the moment."); |
| 1131 | + return null; |
| 1132 | + } |
| 1133 | + |
1119 | 1134 | return { |
1120 | 1135 | agglomerateFileMag, |
1121 | 1136 | getDataValue, |
|
0 commit comments