@@ -14,118 +14,141 @@ import type { Saga } from "viewer/model/sagas/effect-generators";
1414import { select } from "viewer/model/sagas/effect-generators" ;
1515import { ensureWkReady } from "../ready_sagas" ;
1616
17+ const MUTEX_NOT_ACQUIRED_KEY = "MutexCouldNotBeAcquired" ;
18+ const MUTEX_ACQUIRED_KEY = "AnnotationMutexAcquired" ;
19+ const RETRY_COUNT = 12 ;
20+ const ACQUIRE_MUTEX_INTERVAL = 1000 * 60 ;
21+
22+ type MutexLogicState = {
23+ isInitialRequest : boolean ;
24+ doesHaveMutexFromBeginning : boolean ;
25+ doesHaveMutex : boolean ;
26+ shallTryAcquireMutex : boolean ;
27+ } ;
28+
1729export function * acquireAnnotationMutexMaybe ( ) : Saga < void > {
1830 yield * call ( ensureWkReady ) ;
1931 const allowUpdate = yield * select ( ( state ) => state . annotation . restrictions . allowUpdate ) ;
20- const annotationId = yield * select ( ( storeState ) => storeState . annotation . annotationId ) ;
2132 if ( ! allowUpdate ) {
2233 return ;
2334 }
2435 const othersMayEdit = yield * select ( ( state ) => state . annotation . othersMayEdit ) ;
25- const activeUser = yield * select ( ( state ) => state . activeUser ) ;
26- const acquireMutexInterval = 1000 * 60 ;
27- const RETRY_COUNT = 12 ;
28- const MUTEX_NOT_ACQUIRED_KEY = "MutexCouldNotBeAcquired" ;
29- const MUTEX_ACQUIRED_KEY = "AnnotationMutexAcquired" ;
30- let isInitialRequest = true ;
31- let doesHaveMutexFromBeginning = false ;
32- let doesHaveMutex = false ;
33- let shallTryAcquireMutex = othersMayEdit ;
3436
35- function onMutexStateChanged ( canEdit : boolean , blockedByUser : APIUserCompact | null | undefined ) {
36- if ( canEdit ) {
37- Toast . close ( "MutexCouldNotBeAcquired" ) ;
38- if ( ! isInitialRequest ) {
39- const message = (
40- < React . Fragment >
41- { messages [ "annotation.acquiringMutexSucceeded" ] } " "
42- < Button onClick = { ( ) => location . reload ( ) } > Reload the annotation</ Button >
43- </ React . Fragment >
44- ) ;
45- Toast . success ( message , { sticky : true , key : MUTEX_ACQUIRED_KEY } ) ;
46- }
47- } else {
48- Toast . close ( MUTEX_ACQUIRED_KEY ) ;
49- const message =
50- blockedByUser != null
51- ? messages [ "annotation.acquiringMutexFailed" ] ( {
52- userName : `${ blockedByUser . firstName } ${ blockedByUser . lastName } ` ,
53- } )
54- : messages [ "annotation.acquiringMutexFailed.noUser" ] ;
55- Toast . warning ( message , { sticky : true , key : MUTEX_NOT_ACQUIRED_KEY } ) ;
56- }
57- }
37+ const mutexLogicState : MutexLogicState = {
38+ isInitialRequest : true ,
39+ doesHaveMutexFromBeginning : false ,
40+ doesHaveMutex : false ,
41+ shallTryAcquireMutex : othersMayEdit ,
42+ } ;
5843
59- function * tryAcquireMutexContinuously ( ) : Saga < void > {
60- while ( shallTryAcquireMutex ) {
61- if ( isInitialRequest ) {
62- yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
63- }
64- try {
65- const { canEdit, blockedByUser } = yield * retry (
66- RETRY_COUNT ,
67- acquireMutexInterval / RETRY_COUNT ,
68- acquireAnnotationMutex ,
69- annotationId ,
70- ) ;
71- if ( isInitialRequest && canEdit ) {
72- doesHaveMutexFromBeginning = true ;
73- // Only set allow update to true in case the first try to get the mutex succeeded.
74- yield * put ( setAnnotationAllowUpdateAction ( true ) ) ;
75- }
76- if ( ! canEdit || ! doesHaveMutexFromBeginning ) {
77- // If the mutex could not be acquired anymore or the user does not have it from the beginning, set allow update to false.
78- doesHaveMutexFromBeginning = false ;
79- yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
80- }
81- if ( canEdit ) {
82- yield * put ( setBlockedByUserAction ( activeUser ) ) ;
83- } else {
84- yield * put ( setBlockedByUserAction ( blockedByUser ) ) ;
85- }
86- if ( canEdit !== doesHaveMutex || isInitialRequest ) {
87- doesHaveMutex = canEdit ;
88- onMutexStateChanged ( canEdit , blockedByUser ) ;
89- }
90- } catch ( error ) {
91- if ( process . env . IS_TESTING ) {
92- // In unit tests, that explicitly control this generator function,
93- // the console.error after the next yield won't be printed, because
94- // test assertions on the yield will already throw.
95- // Therefore, we also print the error in the test context.
96- console . error ( "Error while trying to acquire mutex:" , error ) ;
97- }
98- const wasCanceled = yield * cancelled ( ) ;
99- if ( ! wasCanceled ) {
100- console . error ( "Error while trying to acquire mutex." , error ) ;
101- yield * put ( setBlockedByUserAction ( undefined ) ) ;
102- yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
103- doesHaveMutexFromBeginning = false ;
104- if ( doesHaveMutex || isInitialRequest ) {
105- onMutexStateChanged ( false , null ) ;
106- doesHaveMutex = false ;
107- }
108- }
109- }
110- isInitialRequest = false ;
111- yield * call ( delay , acquireMutexInterval ) ;
112- }
113- }
114- let runningTryAcquireMutexContinuouslySaga = yield * fork ( tryAcquireMutexContinuously ) ;
44+ let runningTryAcquireMutexContinuouslySaga = yield * fork (
45+ tryAcquireMutexContinuously ,
46+ mutexLogicState ,
47+ ) ;
11548 function * reactToOthersMayEditChanges ( {
11649 othersMayEdit,
11750 } : SetOthersMayEditForAnnotationAction ) : Saga < void > {
118- shallTryAcquireMutex = othersMayEdit ;
119- if ( shallTryAcquireMutex ) {
51+ mutexLogicState . shallTryAcquireMutex = othersMayEdit ;
52+ if ( mutexLogicState . shallTryAcquireMutex ) {
12053 if ( runningTryAcquireMutexContinuouslySaga != null ) {
12154 yield * cancel ( runningTryAcquireMutexContinuouslySaga ) ;
12255 }
123- isInitialRequest = true ;
124- runningTryAcquireMutexContinuouslySaga = yield * fork ( tryAcquireMutexContinuously ) ;
56+ mutexLogicState . isInitialRequest = true ;
57+ runningTryAcquireMutexContinuouslySaga = yield * fork (
58+ tryAcquireMutexContinuously ,
59+ mutexLogicState ,
60+ ) ;
12561 } else {
12662 // othersMayEdit was turned off. The user editing it should be able to edit the annotation.
12763 yield * put ( setAnnotationAllowUpdateAction ( true ) ) ;
12864 }
12965 }
13066 yield * takeEvery ( "SET_OTHERS_MAY_EDIT_FOR_ANNOTATION" , reactToOthersMayEditChanges ) ;
13167}
68+
69+ function * tryAcquireMutexContinuously ( mutexLogicState : MutexLogicState ) : Saga < void > {
70+ const annotationId = yield * select ( ( storeState ) => storeState . annotation . annotationId ) ;
71+ const activeUser = yield * select ( ( state ) => state . activeUser ) ;
72+
73+ while ( mutexLogicState . shallTryAcquireMutex ) {
74+ if ( mutexLogicState . isInitialRequest ) {
75+ yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
76+ }
77+ try {
78+ const { canEdit, blockedByUser } = yield * retry (
79+ RETRY_COUNT ,
80+ ACQUIRE_MUTEX_INTERVAL / RETRY_COUNT ,
81+ acquireAnnotationMutex ,
82+ annotationId ,
83+ ) ;
84+ if ( mutexLogicState . isInitialRequest && canEdit ) {
85+ mutexLogicState . doesHaveMutexFromBeginning = true ;
86+ // Only set allow update to true in case the first try to get the mutex succeeded.
87+ yield * put ( setAnnotationAllowUpdateAction ( true ) ) ;
88+ }
89+ if ( ! canEdit || ! mutexLogicState . doesHaveMutexFromBeginning ) {
90+ // If the mutex could not be acquired anymore or the user does not have it from the beginning, set allow update to false.
91+ mutexLogicState . doesHaveMutexFromBeginning = false ;
92+ yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
93+ }
94+ if ( canEdit ) {
95+ yield * put ( setBlockedByUserAction ( activeUser ) ) ;
96+ } else {
97+ yield * put ( setBlockedByUserAction ( blockedByUser ) ) ;
98+ }
99+ if ( canEdit !== mutexLogicState . doesHaveMutex || mutexLogicState . isInitialRequest ) {
100+ mutexLogicState . doesHaveMutex = canEdit ;
101+ onMutexStateChanged ( mutexLogicState . isInitialRequest , canEdit , blockedByUser ) ;
102+ }
103+ } catch ( error ) {
104+ if ( process . env . IS_TESTING ) {
105+ // In unit tests, that explicitly control this generator function,
106+ // the console.error after the next yield won't be printed, because
107+ // test assertions on the yield will already throw.
108+ // Therefore, we also print the error in the test context.
109+ console . error ( "Error while trying to acquire mutex:" , error ) ;
110+ }
111+ const wasCanceled = yield * cancelled ( ) ;
112+ if ( ! wasCanceled ) {
113+ console . error ( "Error while trying to acquire mutex." , error ) ;
114+ yield * put ( setBlockedByUserAction ( undefined ) ) ;
115+ yield * put ( setAnnotationAllowUpdateAction ( false ) ) ;
116+ mutexLogicState . doesHaveMutexFromBeginning = false ;
117+ if ( mutexLogicState . doesHaveMutex || mutexLogicState . isInitialRequest ) {
118+ onMutexStateChanged ( mutexLogicState . isInitialRequest , false , null ) ;
119+ mutexLogicState . doesHaveMutex = false ;
120+ }
121+ }
122+ }
123+ mutexLogicState . isInitialRequest = false ;
124+ yield * call ( delay , ACQUIRE_MUTEX_INTERVAL ) ;
125+ }
126+ }
127+
128+ function onMutexStateChanged (
129+ isInitialRequest : boolean ,
130+ canEdit : boolean ,
131+ blockedByUser : APIUserCompact | null | undefined ,
132+ ) {
133+ if ( canEdit ) {
134+ Toast . close ( "MutexCouldNotBeAcquired" ) ;
135+ if ( ! isInitialRequest ) {
136+ const message = (
137+ < React . Fragment >
138+ { messages [ "annotation.acquiringMutexSucceeded" ] } " "
139+ < Button onClick = { ( ) => location . reload ( ) } > Reload the annotation</ Button >
140+ </ React . Fragment >
141+ ) ;
142+ Toast . success ( message , { sticky : true , key : MUTEX_ACQUIRED_KEY } ) ;
143+ }
144+ } else {
145+ Toast . close ( MUTEX_ACQUIRED_KEY ) ;
146+ const message =
147+ blockedByUser != null
148+ ? messages [ "annotation.acquiringMutexFailed" ] ( {
149+ userName : `${ blockedByUser . firstName } ${ blockedByUser . lastName } ` ,
150+ } )
151+ : messages [ "annotation.acquiringMutexFailed.noUser" ] ;
152+ Toast . warning ( message , { sticky : true , key : MUTEX_NOT_ACQUIRED_KEY } ) ;
153+ }
154+ }
0 commit comments