11import type { EditableAnnotation } from "admin/rest_api" ;
2- import { acquireAnnotationMutex , editAnnotation } from "admin/rest_api" ;
3- import { Button } from "antd" ;
2+ import { editAnnotation } from "admin/rest_api" ;
43import ErrorHandling from "libs/error_handling" ;
54import Toast from "libs/toast" ;
65import * as Utils from "libs/utils" ;
76import _ from "lodash" ;
87import messages from "messages" ;
9- import React from "react" ;
108import type { ActionPattern } from "redux-saga/effects" ;
11- import {
12- call ,
13- cancel ,
14- cancelled ,
15- delay ,
16- fork ,
17- put ,
18- retry ,
19- take ,
20- takeEvery ,
21- takeLatest ,
22- } from "typed-redux-saga" ;
23- import type { APIUserCompact } from "types/api_types" ;
9+ import { call , delay , put , retry , take , takeLatest } from "typed-redux-saga" ;
2410import constants , { MappingStatusEnum } from "viewer/constants" ;
2511import { getMappingInfo , is2dDataset } from "viewer/model/accessors/dataset_accessor" ;
2612import { getActiveMagIndexForLayer } from "viewer/model/accessors/flycam_accessor" ;
2713import type { Action } from "viewer/model/actions/actions" ;
28- import {
29- type EditAnnotationLayerAction ,
30- type SetAnnotationDescriptionAction ,
31- type SetOthersMayEditForAnnotationAction ,
32- setAnnotationAllowUpdateAction ,
33- setBlockedByUserAction ,
14+ import type {
15+ EditAnnotationLayerAction ,
16+ SetAnnotationDescriptionAction ,
3417} from "viewer/model/actions/annotation_actions" ;
3518import { setVersionRestoreVisibilityAction } from "viewer/model/actions/ui_actions" ;
3619import type { Saga } from "viewer/model/sagas/effect-generators" ;
@@ -48,6 +31,7 @@ import { mayEditAnnotationProperties } from "../accessors/annotation_accessor";
4831import { needsLocalHdf5Mapping } from "../accessors/volumetracing_accessor" ;
4932import { pushSaveQueueTransaction } from "../actions/save_actions" ;
5033import { ensureWkReady } from "./ready_sagas" ;
34+ import { acquireAnnotationMutexMaybe } from "./saving/save_mutex_saga" ;
5135import { updateAnnotationLayerName , updateMetadataOfAnnotation } from "./volume/update_actions" ;
5236
5337/* Note that this must stay in sync with the back-end constant MaxMagForAgglomerateMapping
@@ -237,121 +221,6 @@ export function* watchAnnotationAsync(): Saga<void> {
237221 yield * takeLatest ( "EDIT_ANNOTATION_LAYER" , pushAnnotationLayerUpdateAsync ) ;
238222}
239223
240- export function * acquireAnnotationMutexMaybe ( ) : Saga < void > {
241- yield * call ( ensureWkReady ) ;
242- const allowUpdate = yield * select ( ( state ) => state . annotation . restrictions . allowUpdate ) ;
243- const annotationId = yield * select ( ( storeState ) => storeState . annotation . annotationId ) ;
244- if ( ! allowUpdate ) {
245- return ;
246- }
247- const othersMayEdit = yield * select ( ( state ) => state . annotation . othersMayEdit ) ;
248- const activeUser = yield * select ( ( state ) => state . activeUser ) ;
249- const acquireMutexInterval = 1000 * 60 ;
250- const RETRY_COUNT = 12 ;
251- const MUTEX_NOT_ACQUIRED_KEY = "MutexCouldNotBeAcquired" ;
252- const MUTEX_ACQUIRED_KEY = "AnnotationMutexAcquired" ;
253- let isInitialRequest = true ;
254- let doesHaveMutexFromBeginning = false ;
255- let doesHaveMutex = false ;
256- let shallTryAcquireMutex = othersMayEdit ;
257-
258- function onMutexStateChanged ( canEdit : boolean , blockedByUser : APIUserCompact | null | undefined ) {
259- if ( canEdit ) {
260- Toast . close ( "MutexCouldNotBeAcquired" ) ;
261- if ( ! isInitialRequest ) {
262- const message = (
263- < React . Fragment >
264- { messages [ "annotation.acquiringMutexSucceeded" ] } { " " }
265- < Button onClick = { ( ) => location . reload ( ) } > Reload the annotation</ Button >
266- </ React . Fragment >
267- ) ;
268- Toast . success ( message , { sticky : true , key : MUTEX_ACQUIRED_KEY } ) ;
269- }
270- } else {
271- Toast . close ( MUTEX_ACQUIRED_KEY ) ;
272- const message =
273- blockedByUser != null
274- ? messages [ "annotation.acquiringMutexFailed" ] ( {
275- userName : `${ blockedByUser . firstName } ${ blockedByUser . lastName } ` ,
276- } )
277- : messages [ "annotation.acquiringMutexFailed.noUser" ] ;
278- Toast . warning ( message , { sticky : true , key : MUTEX_NOT_ACQUIRED_KEY } ) ;
279- }
280- }
281-
282- function * tryAcquireMutexContinuously ( ) : Saga < void > {
283- while ( shallTryAcquireMutex ) {
284- if ( isInitialRequest ) {
285- yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
286- }
287- try {
288- const { canEdit, blockedByUser } = yield * retry (
289- RETRY_COUNT ,
290- acquireMutexInterval / RETRY_COUNT ,
291- acquireAnnotationMutex ,
292- annotationId ,
293- ) ;
294- if ( isInitialRequest && canEdit ) {
295- doesHaveMutexFromBeginning = true ;
296- // Only set allow update to true in case the first try to get the mutex succeeded.
297- yield * put ( setAnnotationAllowUpdateAction ( true ) ) ;
298- }
299- if ( ! canEdit || ! doesHaveMutexFromBeginning ) {
300- // If the mutex could not be acquired anymore or the user does not have it from the beginning, set allow update to false.
301- doesHaveMutexFromBeginning = false ;
302- yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
303- }
304- if ( canEdit ) {
305- yield * put ( setBlockedByUserAction ( activeUser ) ) ;
306- } else {
307- yield * put ( setBlockedByUserAction ( blockedByUser ) ) ;
308- }
309- if ( canEdit !== doesHaveMutex || isInitialRequest ) {
310- doesHaveMutex = canEdit ;
311- onMutexStateChanged ( canEdit , blockedByUser ) ;
312- }
313- } catch ( error ) {
314- if ( process . env . IS_TESTING ) {
315- // In unit tests, that explicitly control this generator function,
316- // the console.error after the next yield won't be printed, because
317- // test assertions on the yield will already throw.
318- // Therefore, we also print the error in the test context.
319- console . error ( "Error while trying to acquire mutex:" , error ) ;
320- }
321- const wasCanceled = yield * cancelled ( ) ;
322- if ( ! wasCanceled ) {
323- console . error ( "Error while trying to acquire mutex." , error ) ;
324- yield * put ( setBlockedByUserAction ( undefined ) ) ;
325- yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
326- doesHaveMutexFromBeginning = false ;
327- if ( doesHaveMutex || isInitialRequest ) {
328- onMutexStateChanged ( false , null ) ;
329- doesHaveMutex = false ;
330- }
331- }
332- }
333- isInitialRequest = false ;
334- yield * call ( delay , acquireMutexInterval ) ;
335- }
336- }
337- let runningTryAcquireMutexContinuouslySaga = yield * fork ( tryAcquireMutexContinuously ) ;
338- function * reactToOthersMayEditChanges ( {
339- othersMayEdit,
340- } : SetOthersMayEditForAnnotationAction ) : Saga < void > {
341- shallTryAcquireMutex = othersMayEdit ;
342- if ( shallTryAcquireMutex ) {
343- if ( runningTryAcquireMutexContinuouslySaga != null ) {
344- yield * cancel ( runningTryAcquireMutexContinuouslySaga ) ;
345- }
346- isInitialRequest = true ;
347- runningTryAcquireMutexContinuouslySaga = yield * fork ( tryAcquireMutexContinuously ) ;
348- } else {
349- // othersMayEdit was turned off. The user editing it should be able to edit the annotation.
350- yield * put ( setAnnotationAllowUpdateAction ( true ) ) ;
351- }
352- }
353- yield * takeEvery ( "SET_OTHERS_MAY_EDIT_FOR_ANNOTATION" , reactToOthersMayEditChanges ) ;
354- }
355224export default [
356225 warnAboutSegmentationZoom ,
357226 watchAnnotationAsync ,
0 commit comments