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

Auto save changes in sharing modal #6314

Merged
merged 20 commits into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fb918bf
atuo save changes in sharing modal
MichaelBuessemeyer Jul 5, 2022
e3e49e2
Merge branch 'master' into modal-auto-save
MichaelBuessemeyer Jul 7, 2022
a66dcd8
Merge branch 'master' into modal-auto-save
MichaelBuessemeyer Jul 12, 2022
6aa2467
Merge branch 'master' into modal-auto-save
MichaelBuessemeyer Jul 12, 2022
e0fa506
Merge branch 'master' of github.com:scalableminds/webknossos into mod…
MichaelBuessemeyer Jul 19, 2022
f4f8742
add changelog entry
MichaelBuessemeyer Jul 19, 2022
3496bb2
Merge branch 'master' of github.com:scalableminds/webknossos into mod…
MichaelBuessemeyer Jul 25, 2022
93c5906
apply pr feedback
MichaelBuessemeyer Jul 25, 2022
1700f28
disable sharing settings while backend update call is still in progress
MichaelBuessemeyer Jul 25, 2022
b4e19a9
apply feedback + fix typo
MichaelBuessemeyer Jul 27, 2022
d92ef78
Merge branch 'master' of github.com:scalableminds/webknossos into mod…
MichaelBuessemeyer Jul 27, 2022
5d3b802
add layer name to jobs table entry of tiff export job
MichaelBuessemeyer Jul 27, 2022
183d059
reformulate jobs table entry
MichaelBuessemeyer Jul 27, 2022
028bba6
Merge branch 'master' into modal-auto-save
MichaelBuessemeyer Jul 27, 2022
b2ab071
Merge branch 'master' into add-layer-name-to-tiff-export-table-entry
MichaelBuessemeyer Aug 1, 2022
5320975
Merge branch 'add-layer-name-to-tiff-export-table-entry' of github.co…
MichaelBuessemeyer Aug 1, 2022
10f0abf
Merge branch 'modal-auto-save' of github.com:scalableminds/webknossos…
MichaelBuessemeyer Aug 1, 2022
dd98ed5
handle failed sharing update backend calls properly
MichaelBuessemeyer Aug 1, 2022
c2bb967
remove cancel button
MichaelBuessemeyer Aug 1, 2022
380f0f9
Merge branch 'master' of github.com:scalableminds/webknossos into mod…
MichaelBuessemeyer Aug 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- The NML file in volume annotation download now includes segment metadata like names and anchor positions. [#6347](https://github.com/scalableminds/webknossos/pull/6347)

### Changed
- The sharing modal now automatically saves changes of the sharing options. [#6314](https://github.com/scalableminds/webknossos/pull/6314)
- The Layers tab now displays an Add Skeleton Annotation Layer button with which volume-only annotations can be converted to hybrid annotations. [#6330](https://github.com/scalableminds/webknossos/pull/6330)
- The Zarr directory listings no longer include the current directory “.”. [6359](https://github.com/scalableminds/webknossos/pull/6359)

Expand Down
2 changes: 2 additions & 0 deletions frontend/javascripts/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ instead. Only enable this option if you understand its effect. All layers will n
"annotation.delete": "Do you really want to reset and cancel this annotation?",
"annotation.was_edited": "Successfully updated annotation",
"annotation.shared_teams_edited": "Successfully updated the sharing options for the annotation",
"annotation.shared_teams_edited_failed":
"Updating the sharing options for the annotation failed. Please retry or see the error message in the console.",
"annotation.download": "The following annotation data is available for download immediately.",
"annotation.export":
"Exporting this annotation as TIFF images will trigger a background job to prepare data for download. This may take a while depending on the size of your dataset as well as bounding box and layer selection. You can monitor the progress and start the download from the ",
Expand Down
126 changes: 95 additions & 31 deletions frontend/javascripts/oxalis/view/action-bar/share_modal_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ function _ShareModalView(props: Props) {

const annotationVisibility = tracing.visibility;
const [visibility, setVisibility] = useState(annotationVisibility);
const [isChangingInProgress, setIsChangingInProgress] = useState(false);
const [sharedTeams, setSharedTeams] = useState<APITeam[]>([]);
const sharingToken = useDatasetSharingToken(dataset);

Expand All @@ -164,50 +165,106 @@ function _ShareModalView(props: Props) {
return;
}
const fetchedSharedTeams = await getTeamsForSharedAnnotation(annotationType, annotationId);
console.log("fetchedSharedTeams", fetchedSharedTeams);
setSharedTeams(fetchedSharedTeams);
};

useEffect(() => {
fetchAndSetSharedTeams();
}, [annotationType, annotationId, activeUser]);

const handleCheckboxChange = (event: RadioChangeEvent) => {
setVisibility(event.target.value as any as APIAnnotationVisibility);
};

const handleOthersMayEditCheckboxChange = (event: RadioChangeEvent) => {
const value = event.target.value;
if (typeof value !== "boolean") {
throw new Error("Form element should return boolean value.");
}
const reportSuccessfulChange = (newVisibility: APIAnnotationVisibility) => {
const randomKeyToAllowDuplicates = Math.random().toString(36).substring(0, 5);
philippotto marked this conversation as resolved.
Show resolved Hide resolved
Toast.success(messages["annotation.shared_teams_edited"], {
timeout: 3500,
key: randomKeyToAllowDuplicates,
});

setNewOthersMayEdit(value);
sendAnalyticsEvent("share_annotation", {
visibility: newVisibility,
});
};

const handleOk = async () => {
await editAnnotation(annotationId, annotationType, {
visibility,
const reportFailedChange = () => {
const randomKeyToAllowDuplicates = Math.random().toString(36).substring(0, 5);
Toast.error(messages["annotation.shared_teams_edited_failed"], {
timeout: 3500,
key: randomKeyToAllowDuplicates,
});
Store.dispatch(setAnnotationVisibilityAction(visibility));
};

if (visibility !== "Private") {
const handleCheckboxChange = async (event: RadioChangeEvent) => {
const newVisibility = event.target.value as any as APIAnnotationVisibility;
if (newVisibility === visibility || !hasUpdatePermissions) {
return;
}
setIsChangingInProgress(true);
setVisibility(newVisibility as any as APIAnnotationVisibility);
try {
await editAnnotation(annotationId, annotationType, {
visibility: newVisibility,
});
Store.dispatch(setAnnotationVisibilityAction(newVisibility));
reportSuccessfulChange(newVisibility);
} catch (e) {
console.error("Failed to update the annotations visibility.", e);
// Resetting the visibility to the old value as the request failed
// so the user still sees the settings currently saved in the backend.
setVisibility(visibility as any as APIAnnotationVisibility);
reportFailedChange();
} finally {
setIsChangingInProgress(false);
}
};

const handleSharedTeamsChange = async (value: APITeam | APITeam[]) => {
const newTeams = _.flatten([value]);
if (_.isEqual(newTeams, sharedTeams)) {
return;
}
setIsChangingInProgress(true);
setSharedTeams(newTeams);
try {
await updateTeamsForSharedAnnotation(
annotationType,
annotationId,
sharedTeams.map((team) => team.id),
newTeams.map((team) => team.id),
);
Toast.success(messages["annotation.shared_teams_edited"]);
reportSuccessfulChange(visibility);
} catch (e) {
console.error("Failed to update the annotations shared teams.", e);
// Resetting the shared teams to the old value as the request failed
// so the user still sees the settings currently saved in the backend.
setSharedTeams(sharedTeams);
reportFailedChange();
} finally {
setIsChangingInProgress(false);
}
};

if (newOthersMayEdit !== othersMayEdit) {
await setOthersMayEditForAnnotation(annotationId, annotationType, newOthersMayEdit);
Store.dispatch(setOthersMayEditForAnnotationAction(newOthersMayEdit));
const handleOthersMayEditCheckboxChange = async (event: RadioChangeEvent) => {
const value = event.target.value;
if (typeof value !== "boolean") {
throw new Error("Form element should return boolean value.");
}

sendAnalyticsEvent("share_annotation", {
visibility,
});
onOk();
setIsChangingInProgress(true);
setNewOthersMayEdit(value);
if (value !== othersMayEdit) {
try {
await setOthersMayEditForAnnotation(annotationId, annotationType, value);
Store.dispatch(setOthersMayEditForAnnotationAction(value));
reportSuccessfulChange(visibility);
} catch (e) {
console.error("Failed to update the edit option for others.", e);
// Resetting the others may edit option to the old value as the request failed
// so the user still sees the settings currently saved in the backend.
setNewOthersMayEdit(newOthersMayEdit);
reportFailedChange();
} finally {
setIsChangingInProgress(false);
}
}
};

const maybeShowWarning = () => {
Expand Down Expand Up @@ -252,9 +309,8 @@ function _ShareModalView(props: Props) {
title="Share this annotation"
visible={isVisible}
width={800}
okText={hasUpdatePermissions ? "Save" : "Ok"}
onOk={hasUpdatePermissions ? handleOk : onOk}
onCancel={onOk}
onOk={onOk}
cancelButtonProps={{ style: { display: "none" } }}
>
<Row>
<Col
Expand Down Expand Up @@ -312,7 +368,11 @@ function _ShareModalView(props: Props) {
Who can view this annotation?
</Col>
<Col span={18}>
<RadioGroup onChange={handleCheckboxChange} value={visibility}>
<RadioGroup
onChange={handleCheckboxChange}
value={visibility}
disabled={isChangingInProgress}
>
<Radio style={radioStyle} value="Private" disabled={!hasUpdatePermissions}>
Private
</Radio>
Expand Down Expand Up @@ -372,8 +432,8 @@ function _ShareModalView(props: Props) {
mode="multiple"
allowNonEditableTeams
value={sharedTeams}
onChange={(value) => setSharedTeams(_.flatten([value]))}
disabled={!hasUpdatePermissions || visibility === "Private"}
onChange={handleSharedTeamsChange}
disabled={!hasUpdatePermissions || visibility === "Private" || isChangingInProgress}
/>
<Hint
style={{
Expand All @@ -396,7 +456,11 @@ function _ShareModalView(props: Props) {
Are other users allowed to edit this annotation?
</Col>
<Col span={18}>
<RadioGroup onChange={handleOthersMayEditCheckboxChange} value={newOthersMayEdit}>
<RadioGroup
onChange={handleOthersMayEditCheckboxChange}
value={newOthersMayEdit}
disabled={isChangingInProgress}
>
<Radio style={radioStyle} value={false} disabled={!hasUpdatePermissions}>
No, keep it read-only
</Radio>
Expand Down