|
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) { |
@@ -704,6 +708,7 @@ function* handleProofreadMergeOrMinCut(action: Action) { |
704 | 708 | const allowUpdate = yield* select((state) => state.annotation.restrictions.allowUpdate); |
705 | 709 | if (!allowUpdate) return; |
706 | 710 |
|
| 711 | + const annotationId = yield* select((state) => state.annotation.annotationId); |
707 | 712 | const preparation = yield* call(prepareSplitOrMerge, false); |
708 | 713 | if (!preparation) { |
709 | 714 | return; |
@@ -789,6 +794,7 @@ function* handleProofreadMergeOrMinCut(action: Action) { |
789 | 794 |
|
790 | 795 | yield* put(pushSaveQueueTransaction(items)); |
791 | 796 | yield* call([Model, Model.ensureSavedState]); |
| 797 | + yield* call(releaseAnnotationMutex, annotationId); |
792 | 798 |
|
793 | 799 | if (action.type === "MIN_CUT_AGGLOMERATE") { |
794 | 800 | console.log("start updating the mapping after a min-cut"); |
@@ -893,6 +899,7 @@ function* handleProofreadCutFromNeighbors(action: Action) { |
893 | 899 | const allowUpdate = yield* select((state) => state.annotation.restrictions.allowUpdate); |
894 | 900 | if (!allowUpdate) return; |
895 | 901 |
|
| 902 | + const annotationId = yield* select((state) => state.annotation.annotationId); |
896 | 903 | const preparation = yield* call(prepareSplitOrMerge, false); |
897 | 904 | if (!preparation) { |
898 | 905 | return; |
@@ -947,6 +954,7 @@ function* handleProofreadCutFromNeighbors(action: Action) { |
947 | 954 |
|
948 | 955 | yield* put(pushSaveQueueTransaction(items)); |
949 | 956 | yield* call([Model, Model.ensureSavedState]); |
| 957 | + yield* call(releaseAnnotationMutex, annotationId); |
950 | 958 |
|
951 | 959 | // Now that the changes are saved, we can split the mapping locally (because it requires |
952 | 960 | // communication with the back-end). |
@@ -1130,6 +1138,13 @@ function* prepareSplitOrMerge(isSkeletonProofreading: boolean): Saga<Preparation |
1130 | 1138 | return null; |
1131 | 1139 | } |
1132 | 1140 |
|
| 1141 | + const annotationId = yield* select((state) => state.annotation.annotationId); |
| 1142 | + const { canEdit } = yield* call(acquireAnnotationMutex, annotationId); |
| 1143 | + if (!canEdit) { |
| 1144 | + Toast.error("Could not acquire mutex. Somebody else is proofreading at the moment."); |
| 1145 | + return null; |
| 1146 | + } |
| 1147 | + |
1133 | 1148 | return { |
1134 | 1149 | agglomerateFileMag, |
1135 | 1150 | getDataValue, |
|
0 commit comments