Skip to content

Commit 1ed33f1

Browse files
committed
refactor further
1 parent fef8705 commit 1ed33f1

File tree

1 file changed

+116
-93
lines changed

1 file changed

+116
-93
lines changed

frontend/javascripts/viewer/model/sagas/saving/save_mutex_saga.tsx

Lines changed: 116 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -14,118 +14,141 @@ import type { Saga } from "viewer/model/sagas/effect-generators";
1414
import { select } from "viewer/model/sagas/effect-generators";
1515
import { 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+
1729
export 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

Comments
 (0)