Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix rare crash when viewing shared annotation #6892

Merged
merged 8 commits into from
Mar 9, 2023
43 changes: 31 additions & 12 deletions frontend/javascripts/oxalis/model/sagas/annotation_saga.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
import type { Saga } from "oxalis/model/sagas/effect-generators";
import {
takeLatest,
select,
take,
retry,
delay,
Expand All @@ -31,6 +30,7 @@ import {
cancel,
cancelled,
} from "typed-redux-saga";
import { select } from "oxalis/model/sagas/effect-generators";
import { getMappingInfo } from "oxalis/model/accessors/dataset_accessor";
import { getRequestLogZoomStep } from "oxalis/model/accessors/flycam_accessor";
import { Model } from "oxalis/singletons";
Expand All @@ -40,14 +40,17 @@ import constants, { MappingStatusEnum } from "oxalis/constants";
import messages from "messages";
import { APIUserCompact } from "types/api_flow_types";
import { Button } from "antd";
import ErrorHandling from "libs/error_handling";
import { mayEditAnnotationProperties } from "../accessors/annotation_accessor";

/* Note that this must stay in sync with the back-end constant
compare https://github.com/scalableminds/webknossos/issues/5223 */
const MAX_MAG_FOR_AGGLOMERATE_MAPPING = 16;
export function* pushAnnotationUpdateAsync() {
const tracing = yield* select((state) => state.tracing);

if (!tracing.restrictions.allowUpdate) {
export function* pushAnnotationUpdateAsync(action: Action) {
const tracing = yield* select((state) => state.tracing);
const mayEdit = yield* select((state) => mayEditAnnotationProperties(state));
if (!mayEdit) {
return;
}

Expand All @@ -66,14 +69,30 @@ export function* pushAnnotationUpdateAsync() {
description: tracing.description,
viewConfiguration,
};
yield* retry(
SETTINGS_MAX_RETRY_COUNT,
SETTINGS_RETRY_DELAY,
editAnnotation,
tracing.annotationId,
tracing.annotationType,
editObject,
);
try {
yield* retry(
SETTINGS_MAX_RETRY_COUNT,
SETTINGS_RETRY_DELAY,
editAnnotation,
tracing.annotationId,
tracing.annotationType,
editObject,
);
} catch (error) {
// If the annotation cannot be saved repeatedly (retries will continue for 5 minutes),
// we will only notify the user if the name, visibility or description could not be changed.
// Otherwise, we won't notify the user and won't let the sagas crash as the actual skeleton/volume
// tracings are handled separately.
console.error(error);
ErrorHandling.notify(error as Error);
if (
["SET_ANNOTATION_NAME", "SET_ANNOTATION_VISIBILITY", "SET_ANNOTATION_DESCRIPTION"].includes(
action.type,
)
) {
Toast.error("Could not update annotation property. Please try again.");
}
}
}

function* pushAnnotationLayerUpdateAsync(action: EditAnnotationLayerAction): Saga<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const MAX_SAVE_RETRY_WAITING_TIME = 300000; // 5m
export const UNDO_HISTORY_SIZE = 20;
// Other sagas which simply use the redux-saga's default `retry`,
// need a more simplistic configuration.
export const SETTINGS_RETRY_DELAY = 15 * 1000;
export const SETTINGS_MAX_RETRY_COUNT = 20; // 20 * 15s == 5m
export const SETTINGS_RETRY_DELAY = 3 * 1000;
export const SETTINGS_MAX_RETRY_COUNT = 1; // 20 * 15s == 5m
philippotto marked this conversation as resolved.
Show resolved Hide resolved

export const maximumActionCountPerBatch = 5000;
export const maximumActionCountPerSave = 15000;
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { makeComponentLazy } from "libs/react_helpers";
import { AsyncButton } from "components/async_clickables";
import { PricingEnforcedBlur } from "components/pricing_enforcers";
import { PricingPlanEnum } from "admin/organization/pricing_plan_utils";
import { mayEditAnnotationProperties } from "oxalis/model/accessors/annotation_accessor";

const RadioGroup = Radio.Group;
const sharingActiveNode = true;
Expand Down Expand Up @@ -181,11 +182,10 @@ function _ShareModalView(props: Props) {
const [sharedTeams, setSharedTeams] = useState<APITeam[]>([]);
const sharingToken = useDatasetSharingToken(dataset);

const { owner, othersMayEdit, restrictions } = tracing;
const { othersMayEdit } = tracing;
const [newOthersMayEdit, setNewOthersMayEdit] = useState(othersMayEdit);

const hasUpdatePermissions =
restrictions.allowUpdate && restrictions.allowSave && activeUser && owner?.id === activeUser.id;
const hasUpdatePermissions = useSelector(mayEditAnnotationProperties);
useEffect(() => setVisibility(annotationVisibility), [annotationVisibility]);

const fetchAndSetSharedTeams = async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from "oxalis/view/right-border-tabs/starting_job_modals";
import { formatUserName } from "oxalis/model/accessors/user_accessor";
import { mayUserEditDataset } from "libs/utils";
import { mayEditAnnotationProperties } from "oxalis/model/accessors/annotation_accessor";

const enum StartableJobsEnum {
NUCLEI_INFERRAL = "nuclei inferral",
Expand All @@ -40,6 +41,7 @@ type StateProps = {
task: Task | null | undefined;
activeUser: APIUser | null | undefined;
activeResolution: Vector3;
mayEditAnnotation: boolean;
};
type DispatchProps = {
setAnnotationName: (arg0: string) => void;
Expand Down Expand Up @@ -453,7 +455,7 @@ class DatasetInfoTabView extends React.PureComponent<Props, State> {
{annotationType} : {this.props.task.id}
</span>
);
} else if (!this.props.tracing.restrictions.allowUpdate) {
} else if (!this.props.mayEditAnnotation) {
// For readonly tracings display the non-editable explorative tracing name
annotationTypeLabel = <span>Annotation: {tracingName}</span>;
} else {
Expand All @@ -477,7 +479,7 @@ class DatasetInfoTabView extends React.PureComponent<Props, State> {
const tracingDescription = this.props.tracing.description || "[no description]";
let descriptionEditField;

if (this.props.tracing.restrictions.allowUpdate) {
if (this.props.mayEditAnnotation) {
descriptionEditField = (
<span
style={{
Expand Down Expand Up @@ -658,6 +660,7 @@ const mapStateToProps = (state: OxalisState): StateProps => ({
task: state.task,
activeUser: state.activeUser,
activeResolution: getCurrentResolution(state),
mayEditAnnotation: mayEditAnnotationProperties(state),
});

const mapDispatchToProps = (dispatch: Dispatch<any>) => ({
Expand Down